Synchronized Charts in React Chart component

10 Oct 202324 minutes to read

Tooltip synchronization

The tooltip can be synchronized across multiple charts using the showTooltip and hideTooltip methods. When we hover over a data point in one chart, we call the showTooltip method for the other charts to display related information in other connected charts simultaneously.

In the showTooltip method, specify the following parameters programmatically to enable tooltip for a particular chart:

  • x - Data point x-value or x-coordinate value.
  • y - Data point y-value or y-coordinate value.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Chart, AreaSeries, LineSeries, DateTime, Tooltip, IMouseEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import { Browser } from '@syncfusion/ej2-base';

function App() {
  let chart1;
  let chart2;

  let chart1MouseLeave = (args) => {
    chart2.hideTooltip();
  };

  let chart1MouseMove = (args) => {
    if ((!Browser.isDevice && !chart1.isTouch && !chart1.isChartDrag) || chart1.startMove) {
      chart2.startMove = chart1.startMove;
      chart2.showTooltip(args.x, args.y);
    }
  };

  let chart1MouseUp = (args) => {
    if (Browser.isDevice && chart1.startMove) {
      chart2.hideTooltip();
    }
  };

  let chart2MouseLeave = (args) => {
    chart1.hideTooltip();
  };

  let chart2MouseMove = (args) => {
    if ((!Browser.isDevice && !chart2.isTouch && !chart2.isChartDrag) || chart2.startMove) {
      chart1.startMove = chart2.startMove;
      chart1.showTooltip(args.x, args.y);
    }
  };
  let chart2MouseUp = (args) => {
    if (Browser.isDevice && chart2.startMove) {
      chart1.hideTooltip();
    }
  };
    return <div className="control-section">
    <div className="row">
      <div className="col">
        <ChartComponent
          id="container1"
          ref={chart => chart1 = chart}
          primaryXAxis={{
            minimum: new Date(2023, 1, 18),
            maximum: new Date(2023, 7, 18),
            valueType: 'DateTime',
            labelFormat: 'MMM d',
            lineStyle: { width: 0 },
            majorGridLines: { width: 0 },
            edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
            labelRotation: Browser.isDevice ? -45 : 0,
            interval: Browser.isDevice ? 2 : 1
          }}
          primaryYAxis={{
            labelFormat: 'n2',
            majorTickLines: { width: 0 },
            lineStyle: { width: 0 },
            minimum: 0.86,
            maximum: 0.96,
            interval: 0.025
          }}
          chartArea={{ border: { width: 0 } }}
          chartMouseLeave={chart1MouseLeave.bind(this)}
          chartMouseMove={chart1MouseMove.bind(this)}
          chartMouseUp={chart1MouseUp.bind(this)}
          titleStyle={{ textAlignment: 'Near' }}
          tooltip={{ enable: true, fadeOutDuration: Browser.isDevice ? 2500 : 1000, shared: true, header: '', format: '<b>€${point.y}</b><br>${point.x} 2023', enableMarker: false }}
          title="US to Euro">
          <Inject services={[LineSeries, DateTime, Tooltip]} />
          <SeriesCollectionDirective>
            <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
          </SeriesCollectionDirective>
        </ChartComponent>
      </div>
      <div className="col">
        <ChartComponent
          id="container2"
          ref={chart => chart2 = chart}         
          primaryXAxis={{
            minimum: new Date(2023, 1, 18),
            maximum: new Date(2023, 7, 18),
            valueType: 'DateTime',
            labelFormat: 'MMM d',
            lineStyle: { width: 0 },
            majorGridLines: { width: 0 },
            edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
            labelRotation: Browser.isDevice ? -45 : 0,
            interval: Browser.isDevice ? 2 : 1
          }}
          primaryYAxis={{
            labelFormat: 'n1',
            majorTickLines: { width: 0 },
            lineStyle: { width: 0 },
            minimum: 79,
            maximum: 85,
            interval: 1.5
          }}
          chartArea={{ border: { width: 0 } }}
          chartMouseLeave={chart2MouseLeave.bind(this)}
          chartMouseMove={chart2MouseMove.bind(this)}
          chartMouseUp={chart2MouseUp.bind(this)}
          titleStyle={{ textAlignment: 'Near' }}
          tooltip={{ enable: true, fadeOutDuration: Browser.isDevice ? 2500 : 1000, shared: true, header: '', format: '<b>₹${point.y}</b><br>${point.x} 2023', enableMarker: false }}
          title="US to INR">
          <Inject services={[AreaSeries, DateTime, Tooltip]} />
          <SeriesCollectionDirective>
            <SeriesDirective type="Area" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
          </SeriesCollectionDirective>
        </ChartComponent>
      </div>
    </div>
  </div>
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Chart, AreaSeries, LineSeries, DateTime, Tooltip, IMouseEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import { Browser } from '@syncfusion/ej2-base';

function App() {

  let chart1: ChartComponent;
  let chart2: ChartComponent;

  let chart1MouseLeave = (args: IMouseEventArgs): void => {
    chart2.hideTooltip();
  };

  let chart1MouseMove = (args: IMouseEventArgs): void => {
    if ((!Browser.isDevice && !chart1.isTouch && !chart1.isChartDrag) || chart1.startMove) {
      chart2.startMove = chart1.startMove;
      chart2.showTooltip(args.x, args.y);
    }
  };

  let chart1MouseUp = (args: IMouseEventArgs): void => {
    if (Browser.isDevice && chart1.startMove) {
      chart2.hideTooltip();
    }
  };

  let chart2MouseLeave = (args: IMouseEventArgs): void => {
    chart1.hideTooltip();
  };

  let chart2MouseMove = (args: IMouseEventArgs): void => {
    if ((!Browser.isDevice && !chart2.isTouch && !chart2.isChartDrag) || chart2.startMove) {
      chart1.startMove = chart2.startMove;
      chart1.showTooltip(args.x, args.y);
    }
  };

  let chart2MouseUp = (args: IMouseEventArgs): void => {
    if (Browser.isDevice && chart2.startMove) {
      chart1.hideTooltip();
    }
  }

  return <div className="control-section">
    <div className="row">
      <div className="col">
        <ChartComponent
          id="container1"
          ref={chart => chart1 = chart}
          primaryXAxis={{
            minimum: new Date(2023, 1, 18),
            maximum: new Date(2023, 7, 18),
            valueType: 'DateTime',
            labelFormat: 'MMM d',
            lineStyle: { width: 0 },
            majorGridLines: { width: 0 },
            edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
            labelRotation: Browser.isDevice ? -45 : 0,
            interval: Browser.isDevice ? 2 : 1
          }}
          primaryYAxis={{
            labelFormat: 'n2',
            majorTickLines: { width: 0 },
            lineStyle: { width: 0 },
            minimum: 0.86,
            maximum: 0.96,
            interval: 0.025
          }}
          chartArea={{ border: { width: 0 } }}
          chartMouseLeave={chart1MouseLeave.bind(this)}
          chartMouseMove={chart1MouseMove.bind(this)}
          chartMouseUp={chart1MouseUp.bind(this)}
          titleStyle={{ textAlignment: 'Near' }}
          tooltip={{ enable: true, fadeOutDuration: Browser.isDevice ? 2500 : 1000, shared: true, header: '', format: '<b>€${point.y}</b><br>${point.x} 2023', enableMarker: false }}
          title="US to Euro">
          <Inject services={[LineSeries, DateTime, Tooltip]} />
          <SeriesCollectionDirective>
            <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
          </SeriesCollectionDirective>
        </ChartComponent>
      </div>
      <div className="col">
        <ChartComponent
          id="container2"
          ref={chart => chart2 = chart}         
          primaryXAxis={{
            minimum: new Date(2023, 1, 18),
            maximum: new Date(2023, 7, 18),
            valueType: 'DateTime',
            labelFormat: 'MMM d',
            lineStyle: { width: 0 },
            majorGridLines: { width: 0 },
            edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
            labelRotation: Browser.isDevice ? -45 : 0,
            interval: Browser.isDevice ? 2 : 1
          }}
          primaryYAxis={{
            labelFormat: 'n1',
            majorTickLines: { width: 0 },
            lineStyle: { width: 0 },
            minimum: 79,
            maximum: 85,
            interval: 1.5
          }}
          chartArea={{ border: { width: 0 } }}
          chartMouseLeave={chart2MouseLeave.bind(this)}
          chartMouseMove={chart2MouseMove.bind(this)}
          chartMouseUp={chart2MouseUp.bind(this)}
          titleStyle={{ textAlignment: 'Near' }}
          tooltip={{ enable: true, fadeOutDuration: Browser.isDevice ? 2500 : 1000, shared: true, header: '', format: '<b>₹${point.y}</b><br>${point.x} 2023', enableMarker: false }}
          title="US to INR">
          <Inject services={[AreaSeries, DateTime, Tooltip]} />
          <SeriesCollectionDirective>
            <SeriesDirective type="Area" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
          </SeriesCollectionDirective>
        </ChartComponent>
      </div>
    </div>
  </div>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));

Crosshair synchronization

The crosshair can be synchronized across multiple charts using the showCrosshair and hideCrosshair methods. When we hover over one chart, we call the showCrosshair method for the other charts to align with data points in other connected charts, simplifying data comparison and analysis.

In the showCrosshair method, specify the following parameters programmatically to enable crosshair for a particular chart:

  • x - Specifies the x-value of the point or x-coordinate.
  • y - Specifies the y-value of the point or y-coordinate.
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Chart, AreaSeries, SplineSeries, DateTime, Crosshair, IMouseEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import { Browser } from '@syncfusion/ej2-base';

function App() {
    let chart1;
    let chart2;

    let chart1MouseLeave = (args) => {
        chart2.hideCrosshair();
    };

    let chart1MouseMove = (args) => {
        if ((!Browser.isDevice && !chart1.isTouch && !chart1.isChartDrag) || chart1.startMove) {
            chart2.startMove = chart1.startMove;
            chart2.showCrosshair(args.x, args.y);
        }
    };

    let chart1MouseUp = (args) => {
        if (Browser.isDevice && chart1.startMove) {
            chart2.hideCrosshair();
        }
    };

    let chart2MouseLeave = (args) => {
        chart1.hideCrosshair();
    };

    let chart2MouseMove = (args) => {
        if ((!Browser.isDevice && !chart2.isTouch && !chart2.isChartDrag) || chart2.startMove) {
            chart1.startMove = chart2.startMove;
            chart1.showCrosshair(args.x, args.y);
        }
    };
    let chart2MouseUp = (args) => {
        if (Browser.isDevice && chart2.startMove) {
            chart1.hideCrosshair();
        }
    };
    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart => chart1 = chart}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1,
                        crosshairTooltip: { enable: true }
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    chartMouseLeave={chart1MouseLeave.bind(this)}
                    chartMouseMove={chart1MouseMove.bind(this)}
                    chartMouseUp={chart1MouseUp.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    crosshair={{ enable: true, lineType: 'Vertical', dashArray: '2,2' }}
                    title="US to Euro">
                    <Inject services={[SplineSeries, DateTime, Crosshair]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Spline" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart => chart2 = chart}                  
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1,
                        crosshairTooltip: { enable: true }
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    chartMouseLeave={chart2MouseLeave.bind(this)}
                    chartMouseMove={chart2MouseMove.bind(this)}
                    chartMouseUp={chart2MouseUp.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    crosshair={{ enable: true, lineType: 'Vertical', dashArray: '2,2' }}
                    title="US to INR">
                    <Inject services={[AreaSeries, DateTime, Crosshair]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Area" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Chart, AreaSeries, SplineSeries, DateTime, Crosshair, IMouseEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import { Browser } from '@syncfusion/ej2-base';

function App() {

    let chart1: ChartComponent;
    let chart2: ChartComponent;

    let chart1MouseLeave = (args: IMouseEventArgs): void => {
        chart2.hideCrosshair();
    };

    let chart1MouseMove = (args: IMouseEventArgs): void => {
        if ((!Browser.isDevice && !chart1.isTouch && !chart1.isChartDrag) || chart1.startMove) {
            chart2.startMove = chart1.startMove;
            chart2.showCrosshair(args.x, args.y);
        }
    };

    let chart1MouseUp = (args: IMouseEventArgs): void => {
        if (Browser.isDevice && chart1.startMove) {
            chart2.hideCrosshair();
        }
    };

    let chart2MouseLeave = (args: IMouseEventArgs): void => {
        chart1.hideCrosshair();
    };

    let chart2MouseMove = (args: IMouseEventArgs): void => {
        if ((!Browser.isDevice && !chart2.isTouch && !chart2.isChartDrag) || chart2.startMove) {
            chart1.startMove = chart2.startMove;
            chart1.showCrosshair(args.x, args.y);
        }
    };

    let chart2MouseUp = (args: IMouseEventArgs): void => {
        if (Browser.isDevice && chart2.startMove) {
            chart1.hideCrosshair();
        }
    }

    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart => chart1 = chart}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1,
                        crosshairTooltip: { enable: true }
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    chartMouseLeave={chart1MouseLeave.bind(this)}
                    chartMouseMove={chart1MouseMove.bind(this)}
                    chartMouseUp={chart1MouseUp.bind(this)}
                    crosshair={{ enable: true, lineType: 'Vertical', dashArray: '2,2' }}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to Euro">
                    <Inject services={[SplineSeries, DateTime, Crosshair]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Spline" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart => chart2 = chart}                    
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1,
                        crosshairTooltip: { enable: true }
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    chartMouseLeave={chart2MouseLeave.bind(this)}
                    chartMouseMove={chart2MouseMove.bind(this)}
                    chartMouseUp={chart2MouseUp.bind(this)}
                    crosshair={{ enable: true, lineType: 'Vertical', dashArray: '2,2' }}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to INR">
                    <Inject services={[AreaSeries, DateTime, Crosshair]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Area" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>
};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));

Zooming synchronization

You can maintain constant zoom levels across multiple charts using the zoomComplete event. In the zoomComplete event, obtain the zoomFactor and zoomPosition values of the particular chart, and then apply those values to the other charts.

import * as ReactDOM from "react-dom";
import { Chart, SplineAreaSeries, LineSeries, DateTime, Zoom, IZoomCompleteEventArgs, Selection, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import * as React from 'react';
import { Browser } from '@syncfusion/ej2-base';

function App() {
    let chart1 = React.useRef(null);
    let chart2 = React.useRef(null);

    let charts = [];
    React.useEffect(() => {
        charts = [chart1.current, chart2.current];
    }, []);
    let zoomFactor = 0;
    let zoomPosition = 0;

    let zoomComplete = (args) => {
        if (args.axis.name === 'primaryXAxis') {
            zoomFactor = args.currentZoomFactor;
            zoomPosition = args.currentZoomPosition;
            zoomCompleteFunction(args);
        }
    };

    let zoomCompleteFunction = (args) => {
        for (let i = 0; i < charts.length; i++) {
            if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
                charts[i].primaryXAxis.zoomFactor = zoomFactor;
                charts[i].primaryXAxis.zoomPosition = zoomPosition;
                charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
                charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
            }
        }
    }
    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart1}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableMouseWheelZooming: true,
                        enablePinchZooming: true,
                        enableScrollbar: false,
                        enableDeferredZooming: false,
                        enableSelectionZooming: true,
                        enablePan: true,
                        mode: 'X',
                        toolbarItems: ['Pan', 'Reset']
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to Euro">
                    <Inject services={[LineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart2}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableMouseWheelZooming: true,
                        enablePinchZooming: true,
                        enableScrollbar: false,
                        enableDeferredZooming: false,
                        enableSelectionZooming: true,
                        enablePan: true,
                        mode: 'X',
                        toolbarItems: ['Pan', 'Reset']
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to INR">
                    <Inject services={[SplineAreaSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="SplineArea" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));
import * as ReactDOM from "react-dom";
import { Chart, SplineAreaSeries, LineSeries, DateTime, Zoom, IZoomCompleteEventArgs, Selection, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import * as React from "react";
import { Browser } from '@syncfusion/ej2-base';


function App() {

    let chart1 = React.useRef<ChartComponent>(null);
    let chart2 = React.useRef<ChartComponent>(null);

    let charts: ChartComponent[] = [];
    React.useEffect(() => {
        charts = [chart1.current, chart2.current];
    }, []);
    let zoomFactor: number = 0;
    let zoomPosition: number = 0;

    let zoomComplete = (args: IZoomCompleteEventArgs): void => {
        if (args.axis.name === 'primaryXAxis') {
            zoomFactor = args.currentZoomFactor;
            zoomPosition = args.currentZoomPosition;
            zoomCompleteFunction(args);
        }
    };

    let zoomCompleteFunction = (args: IZoomCompleteEventArgs): void => {
        for (let i: number = 0; i < charts.length; i++) {
            if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
                charts[i].primaryXAxis.zoomFactor = zoomFactor;
                charts[i].primaryXAxis.zoomPosition = zoomPosition;
                charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
                charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
            }
        }
    }

    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart1}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableMouseWheelZooming: true,
                        enablePinchZooming: true,
                        enableScrollbar: false,
                        enableDeferredZooming: false,
                        enableSelectionZooming: true,
                        enablePan: true,
                        mode: 'X',
                        toolbarItems: ['Pan', 'Reset']
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to Euro">
                    <Inject services={[LineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart2}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableMouseWheelZooming: true,
                        enablePinchZooming: true,
                        enableScrollbar: false,
                        enableDeferredZooming: false,
                        enableSelectionZooming: true,
                        enablePan: true,
                        mode: 'X',
                        toolbarItems: ['Pan', 'Reset']
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to INR">
                    <Inject services={[SplineAreaSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="SplineArea" dataSource={synchronizedData} xName="USD" yName="INR" opacity={0.6} width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>

};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));

Selection synchronization

You can select the data across multiple charts using the selectionComplete event. In the selectionComplete event, obtain the selected values of the particular chart, and then apply those values to the other charts.

import * as ReactDOM from "react-dom";
import { Chart, SplineSeries, LineSeries, DateTime, Zoom, IZoomCompleteEventArgs, Selection, ISelectionCompleteEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import * as React from 'react';
import { Browser } from '@syncfusion/ej2-base';

function App() {
    let chart1 = React.useRef(null);
    let chart2 = React.useRef(null);
    let charts = [];
    React.useEffect(() => {
        charts = [chart1.current, chart2.current];
    }, []);
    let zoomFactor = 0;
    let zoomPosition = 0;
    let count = 0;

    let zoomComplete = (args) => {
        if (args.axis.name === 'primaryXAxis') {
            zoomFactor = args.currentZoomFactor;
            zoomPosition = args.currentZoomPosition;
            zoomCompleteFunction(args);
        }
    };

    let zoomCompleteFunction = (args) => {
        for (let i = 0; i < charts.length; i++) {
            if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
                charts[i].primaryXAxis.zoomFactor = zoomFactor;
                charts[i].primaryXAxis.zoomPosition = zoomPosition;
                charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
                charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
            }
        }
    };

    let selectionComplete = (args) => {
        selectionCompleteFunction(args);
    };

    let selectionCompleteFunction = (args) => {
        if (count == 0) {
            for (var j = 0; j < args.selectedDataValues.length; j++) {
                args.selectedDataValues[j].point = args.selectedDataValues[j].pointIndex;
                args.selectedDataValues[j].series = args.selectedDataValues[j].seriesIndex;
            }
            for (var i = 0; i < charts.length; i++) {
                if (args.chart.element.id !== charts[i].element.id) {
                    charts[i].selectedDataIndexes = args.selectedDataValues;
                    count += 1;
                    charts[i].dataBind();
                }
            }
            count = 0;
        }
    };
    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart1}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableSelectionZooming: true,
                        mode: 'X'
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    selectionComplete={selectionComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to Euro"
                    selectionMode='Point'
                    selectionPattern='Box'>
                    <Inject services={[LineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart2}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableSelectionZooming: true,
                        mode: 'X'
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    selectionComplete={selectionComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to INR"
                    selectionMode='Point'
                    selectionPattern='Box'>
                    <Inject services={[SplineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Spline" dataSource={synchronizedData} xName="USD" yName="INR" width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>
}
;
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));
import * as ReactDOM from "react-dom";
import { Chart, SplineSeries, LineSeries, DateTime, Zoom, IZoomCompleteEventArgs, Selection, ISelectionCompleteEventArgs, ChartComponent, SeriesCollectionDirective, SeriesDirective, Inject } from '@syncfusion/ej2-react-charts';
import { synchronizedData } from 'datasource.ts';
import * as React from "react";
import { Browser } from '@syncfusion/ej2-base';

function App() {

    let chart1 = React.useRef<ChartComponent>(null);
    let chart2 = React.useRef<ChartComponent>(null);

    let charts: ChartComponent[] = [];
    React.useEffect(() => {
        charts = [chart1.current, chart2.current];
    }, []);
    let zoomFactor: number = 0;
    let zoomPosition: number = 0;
    let count: number = 0;

    let zoomComplete = (args: IZoomCompleteEventArgs): void => {
        if (args.axis.name === 'primaryXAxis') {
            zoomFactor = args.currentZoomFactor;
            zoomPosition = args.currentZoomPosition;
            zoomCompleteFunction(args);
        }
    };

    let zoomCompleteFunction = (args: IZoomCompleteEventArgs): void => {
        for (let i: number = 0; i < charts.length; i++) {
            if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
                charts[i].primaryXAxis.zoomFactor = zoomFactor;
                charts[i].primaryXAxis.zoomPosition = zoomPosition;
                charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
                charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
            }
        }
    };

    let selectionComplete = (args: ISelectionCompleteEventArgs): void => {
        selectionCompleteFunction(args);
    };

    let selectionCompleteFunction = (args: ISelectionCompleteEventArgs): void => {
        if (count == 0) {
            for (var j = 0; j < args.selectedDataValues.length; j++) {
                args.selectedDataValues[j].point = args.selectedDataValues[j].pointIndex;
                args.selectedDataValues[j].series = args.selectedDataValues[j].seriesIndex;
            }
            for (var i = 0; i < charts.length; i++) {
                if (args.chart.element.id !== charts[i].element.id) {
                    charts[i].selectedDataIndexes = args.selectedDataValues;
                    count += 1;
                    charts[i].dataBind();
                }
            }
            count = 0;
        }
    };

    return <div className="control-section">
        <div className="row">
            <div className="col">
                <ChartComponent
                    id="container1"
                    ref={chart1}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n2',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 0.86,
                        maximum: 0.96,
                        interval: 0.025
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableSelectionZooming: true,
                        mode: 'X'
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    selectionComplete={selectionComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to Euro"
                    selectionMode='Point'
                    selectionPattern='Box'>
                    <Inject services={[LineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Line" dataSource={synchronizedData} xName="USD" yName="EUR" width={2} emptyPointSettings={{ mode: 'Drop' }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
            <div className="col">
                <ChartComponent
                    id="container2"
                    ref={chart2}
                    primaryXAxis={{
                        minimum: new Date(2023, 1, 18),
                        maximum: new Date(2023, 7, 18),
                        valueType: 'DateTime',
                        labelFormat: 'MMM d',
                        lineStyle: { width: 0 },
                        majorGridLines: { width: 0 },
                        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
                        labelRotation: Browser.isDevice ? -45 : 0,
                        interval: Browser.isDevice ? 2 : 1
                    }}
                    primaryYAxis={{
                        labelFormat: 'n1',
                        majorTickLines: { width: 0 },
                        lineStyle: { width: 0 },
                        minimum: 79,
                        maximum: 85,
                        interval: 1.5
                    }}
                    chartArea={{ border: { width: 0 } }}
                    zoomSettings={{
                        enableSelectionZooming: true,
                        mode: 'X'
                    }}
                    zoomComplete={zoomComplete.bind(this)}
                    selectionComplete={selectionComplete.bind(this)}
                    titleStyle={{ textAlignment: 'Near' }}
                    title="US to INR"
                    selectionMode='Point'
                    selectionPattern='Box'>
                    <Inject services={[SplineSeries, DateTime, Zoom, Selection]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective type="Spline" dataSource={synchronizedData} xName="USD" yName="INR" width={2} border={{ width: 2 }}></SeriesDirective>
                    </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        </div>
    </div>

};
export default App;
ReactDOM.render(<App />, document.getElementById("charts"));