AG Charts allows zooming into charts, making it easier to navigate large datasets.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
},
tooltip: {
enabled: false,
},
axes: {
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
To enable this feature, set zoom.enabled to true.
{
zoom: {
enabled: true,
},
}In the above example you can:
- Scroll in and out with the mouse wheel or trackpad.
- Zoom and pan using touch and multi-touch functionality.
- Use + and - keys to zoom in or out (when in focus).
- Click and drag the mouse to pan around the zoomed in chart.
- Click (or touch) and drag an axis to zoom in or out on only that axis.
- Double click (or double tap) anywhere to reset the zoom.
- Double click (or double tap) an axis to reset the zoom on only that axis.
If axis[].tick.maxSpacing is provided, the axis ticks and labels will update with the zoom.
Scrolling Copy Link
This allows zooming by using the mouse wheel or trackpad, as shown in the above example and is enabled by default. To disable, use enableScrolling: false.
Anchor Point Copy Link
By default, the chart will zoom while keeping the right side of the x-axis pinned. You can change this anchor point with the anchorPointX and anchorPointY properties, setting them each to one of:
start, the left or bottom of the chart when scrolling on the x-axis or y-axis respectively,middle(default for y-axis), the middle of the chart,end(default for x-axis), the right or top of the chart when scrolling on the x-axis or y-axis respectively,pointer, keep the mouse pointer above the same position on the chart when zooming.
In the example below, we set the anchor point for both axes to the mouse pointer.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
CategoryAxisModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CategoryAxisModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
axes: "xy",
anchorPointX: "pointer",
anchorPointY: "pointer",
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
anchorPointX: 'pointer',
anchorPointY: 'pointer',
},
} Scrolling Step Copy Link
When scrolling, the chart zooms in by a single step for each movement of the scroll wheel or trackpad. By default scrollingStep is set to 0.1, or 10% of the chart each time.
In the example below, we change the step to 0.4.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
scrollingStep: 0.4,
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
scrollingStep: 0.4,
},
} Axes Copy Link
By default, scrolling zoom is only enabled for the x axis. This can be changed by setting the axes property to x, y or xy.
In the example below, we enable zoom on both the x and y axes.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
CategoryAxisModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CategoryAxisModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
axes: "xy",
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
axes: 'xy',
},
} Scrolling Mode Copy Link
By default, vertical mouse wheel or trackpad scrolling zooms the chart. Set scrollingMode: 'pan' to pan instead.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
BarSeriesModule,
CategoryAxisModule,
ContextMenuModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
AnimationModule,
BarSeriesModule,
CategoryAxisModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
scrollingMode: "zoom",
},
title: {
text: "Population by Country (millions)",
},
initialState: {
zoom: {
ratioY: { start: 0.6 },
},
},
axes: {
x: {
type: "number",
label: {
formatter: (params) => `${params.value}M`,
},
},
},
data: getData(),
series: [
{
type: "bar",
direction: "horizontal",
xKey: "country",
yKey: "population",
yName: "Population (millions)",
},
],
});
const setScrollingMode = (mode: "zoom" | "pan") => {
const nextOptions = clone(options);
nextOptions.zoom!.scrollingMode = mode;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={() => setScrollingMode("zoom")}>Zoom</button>
<button onClick={() => setScrollingMode("pan")}>Pan</button>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
export function getData() {
return [
{ country: "China", population: 1425 },
{ country: "India", population: 1417 },
{ country: "United States", population: 335 },
{ country: "Indonesia", population: 277 },
{ country: "Pakistan", population: 230 },
{ country: "Nigeria", population: 224 },
{ country: "Brazil", population: 216 },
{ country: "Bangladesh", population: 173 },
{ country: "Russia", population: 144 },
{ country: "Mexico", population: 129 },
{ country: "Ethiopia", population: 127 },
{ country: "Japan", population: 124 },
{ country: "Philippines", population: 117 },
{ country: "Egypt", population: 112 },
{ country: "DR Congo", population: 102 },
{ country: "Vietnam", population: 99 },
{ country: "Iran", population: 89 },
{ country: "Turkey", population: 86 },
{ country: "Germany", population: 84 },
{ country: "Thailand", population: 72 },
{ country: "United Kingdom", population: 68 },
{ country: "Tanzania", population: 66 },
{ country: "France", population: 65 },
{ country: "South Africa", population: 60 },
{ country: "Italy", population: 59 },
];
}
{
zoom: {
scrollingMode: 'pan',
},
} Panning Copy Link
This is enabled by default and allows users to click and drag to move around a zoomed chart. To disable, use enablePanning: false.
If zoom by selecting is enabled, clicking and dragging will no longer pan by default. Instead the user will need to hold down a key to switch to panning mode.
This key defaults to alt but can be set with the panKey property to one of alt, ctrl, shift or meta (the command key on MacOS or start key on Windows).
In the example below, panning can only be done by holding down the shift key while clicking and dragging.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
enableSelecting: true,
panKey: "shift",
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
panKey: 'shift',
},
} Selecting Copy Link
This method of zooming works by clicking and dragging a box to select an area on the chart. This is disabled by default. To enable, use enableSelecting: true.
In the example below, the user can only zoom in by selection, and can only zoom out by double-click to reset.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enableAxisDragging: false,
enablePanning: false,
enableScrolling: false,
enableSelecting: true,
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
enableAxisDragging: false,
enablePanning: false,
enableScrolling: false,
enableSelecting: true,
},
} Two Finger Zoom-Pan Copy Link
By default, using two fingers to pinch in or out will zoom the chart. It is also possible to use two fingers to pan a zoomed chart.
To disable this behaviour, use enableTwoFingerZoom: false.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enableTwoFingerZoom: false,
},
initialState: {
zoom: {
ratioX: { start: 0.48, end: 0.52 },
ratioY: { start: 0.21, end: 0.82 },
},
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
enableTwoFingerZoom: false,
},
}In the above example:
- Two fingers gestures are not consumed by the chart. Instead they zoom or scroll the entire page.
Axis Zoom Controls Copy Link
By default, a user can click and drag on any axis to change the zoom of that axis. This ignores the axes property and is enabled by default for all axes.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
AnimationModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
enabled: true,
enableAxisDragging: true,
enableAxisScrolling: true,
axisDraggingMode: "zoom",
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
position: "left",
title: {
text: "Spending",
},
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
ySecondary: {
type: "number",
position: "right",
title: {
text: "Tonnes",
},
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
{
type: "line",
xKey: "year",
yKey: "tonnes",
yKeyAxis: "ySecondary",
},
],
});
const setAxisDragging = (mode: "zoom" | "pan") => {
const nextOptions = clone(options);
nextOptions.zoom!.axisDraggingMode = mode;
setOptions(nextOptions);
};
const enableAxisDragging = (enabled: "on" | "off") => {
const nextOptions = clone(options);
nextOptions.zoom!.enableAxisDragging = enabled === "on";
setOptions(nextOptions);
};
const enableAxisScrolling = (enabled: "on" | "off") => {
const nextOptions = clone(options);
nextOptions.zoom!.enableAxisScrolling = enabled === "on";
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<label htmlFor="enable-dragging">Axis Dragging:</label>
<select
id="enable-dragging"
onChange={(event) => enableAxisDragging(event.target.value)}
className="gap-right"
>
<option value="on">Enabled</option>
<option value="off">Disabled</option>
</select>
<label htmlFor="dragging-mode">Axis Dragging Mode:</label>
<select
id="dragging-mode"
onChange={(event) => setAxisDragging(event.target.value)}
className="gap-right"
>
<option value="zoom">Zoom</option>
<option value="pan">Pan</option>
</select>
<label htmlFor="enable-scrolling">Axis Scrolling:</label>
<select
id="enable-scrolling"
onChange={(event) => enableAxisScrolling(event.target.value)}
>
<option value="on">Enabled</option>
<option value="off">Disabled</option>
</select>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number; tonnes: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: new Date().getFullYear() - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
tonnes: 0,
});
}
// Add tonnes separately to ensure the same randomisation is used for spending as other examples
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data[i] = {
...data[i],
tonnes:
i === 0 ? random() * 1000 : data[i - 1].tonnes + random() * 10 - 5,
};
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
- When using
axisDraggingMode: 'zoom'(default), dragging either of the y-axes will zoom both of them. - Use
axisDraggingMode: 'pan'to pan while dragging an axis. - Use
enableAxisDragging: falseto disable all axis dragging. - Scrolling on an axis to zoom is enabled by default. Use
enableAxisScrolling: falseto disable it.
Double-Click to Reset Copy Link
This allows users to reset the zoom by double-clicking in an empty space in the chart area, and is enabled by default. To disable, use enableDoubleClickToReset: false.
Minimum Visible Items Copy Link
The minVisibleItems option can be used to limit how far a user can zoom in to the chart.
The example below demonstrates setting minVisibleItems to 10, preventing the user from zooming beyond showing a minimum of 10 points on the line.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
ModuleRegistry.registerModules([
AnimationModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
ZoomModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
zoom: {
minVisibleItems: 10,
},
tooltip: {
enabled: false,
},
axes: {
y: {
type: "number",
interval: {
minSpacing: 80,
maxSpacing: 120,
},
},
x: {
type: "number",
nice: false,
interval: {
minSpacing: 80,
maxSpacing: 120,
},
label: {
autoRotate: false,
},
},
},
data: getData(),
series: [
{
type: "line",
xKey: "year",
yKey: "spending",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const NUM_DATA_POINTS = 400;
export function getData() {
const data: Array<{ year: number; spending: number }> = [];
for (let i = 0; i < NUM_DATA_POINTS; i++) {
data.push({
year: 2025 - NUM_DATA_POINTS + i,
spending:
i === 0 ? random() * 100 : data[i - 1].spending + random() * 10 - 5,
});
}
return data;
}
let seed = 1234;
function random() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
{
zoom: {
minVisibleItems: 10,
},
} Auto Scaling Copy Link
Auto Scaling dynamically adjusts the y-axis to fit the visible data whenever the x-axis is zoomed or panned. This is enabled by default when zooming the x-axis. To disable, use autoScaling: false.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AnimationModule,
CandlestickSeriesModule,
ContextMenuModule,
CrosshairModule,
LegendModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
OrdinalTimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
AnimationModule,
CandlestickSeriesModule,
CrosshairModule,
LegendModule,
NavigatorModule,
NumberAxisModule,
OrdinalTimeAxisModule,
ZoomModule,
ContextMenuModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
data: getData(1e3),
animation: { enabled: false },
zoom: {
enabled: true,
anchorPointX: "pointer",
anchorPointY: "pointer",
autoScaling: {
enabled: true,
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
series: [
{
type: "candlestick",
xKey: "timestamp",
lowKey: "low",
highKey: "high",
openKey: "open",
closeKey: "close",
},
],
});
const setAutoScaling = (enabled: boolean) => {
const nextOptions = clone(options);
nextOptions.zoom!.autoScaling!.enabled = enabled;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={() => setAutoScaling(false)}>Disabled</button>
<button onClick={() => setAutoScaling(true)}>Enabled</button>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
const startPrice = 100;
const maxDailyPriceChange = 5;
const maxRangeDelta = 1;
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
export function getData(days: number) {
let currentPrice = startPrice;
const random = seedRandom();
return Array.from({ length: days }, (_, i) => {
// Note time is reversed
const close = currentPrice;
const open = close + (random() * 2 - 1) * maxDailyPriceChange;
currentPrice = open;
const high = Math.max(open, close) + random() * maxRangeDelta;
const low = Math.min(open, close) - random() * maxRangeDelta;
const timestamp = new Date(2024, 0, 0 /* End of 2023 */, -i);
return { timestamp, open, close, high, low };
}).reverse();
}
{
zoom: {
autoScaling: {
enabled: false,
},
},
}In the above example:
- Zoom in to the chart by scrolling and then pan left and right.
- Observe how the vertical axis domain changes to fit the displayed data range.
Auto Scaling is never applied when the user has manually adjusted the y-axis by dragging it. Auto Scaling will be reapplied when the y-axis is reset, for example by double clicking on it.
On Data Change Copy Link
When data is updated while the chart is zoomed, the zoom.onDataChange options control how the zoomed view adjusts. This helps users maintain their focus when data changes, or always see the latest data.
The default strategy is preserveDomain, use onDataChange.strategy to change to one of the below.
Preserve Domain Copy Link
strategy: 'preserveDomain' preserves the current axis domain when data changes, keeping the view at the exact domain values if possible.
This is useful for appending data without disrupting users viewing a specific time period in the middle of the data.
import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AgChartsInstance,
LineSeriesModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData, getNextDataPoint } from "./data";
import clone from "clone";
let data = getData();
let streamingInterval: ReturnType<typeof setInterval> | null = null;
ModuleRegistry.registerModules([
LineSeriesModule,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
]);
const ChartExample = () => {
const chartRef = useRef<AgChartsInstance>(null);
const [options, setOptions] = useState<AgCartesianChartOptions>({
data,
series: [
{
type: "line",
xKey: "date",
yKey: "price",
marker: { enabled: false },
},
],
axes: {
x: {
type: "time",
nice: false,
},
y: {
type: "number",
title: { text: "Price" },
},
},
zoom: {
enabled: true,
onDataChange: {
strategy: "preserveDomain",
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
initialState: {
zoom: {
ratioX: { start: 0.3, end: 0.7 },
},
},
});
const startUpdates = () => {
if (streamingInterval) return;
streamingInterval = setInterval(() => {
const nextPoint = getNextDataPoint(data);
data.push(nextPoint);
chartRef.current!.applyTransaction({ add: [nextPoint] });
}, 100);
};
const stopUpdates = () => {
if (streamingInterval) {
clearInterval(streamingInterval);
streamingInterval = null;
}
};
const resetData = () => {
const nextOptions = clone(options);
stopUpdates();
data = getData();
nextOptions.data = data;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={startUpdates}>Start Updates</button>
<button onClick={stopUpdates}>Stop Updates</button>
<button onClick={resetData}>Reset</button>
</div>
</div>
<AgCharts ref={chartRef} options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef;
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
const random = seedRandom(12345);
export interface DataPoint {
date: Date;
price: number;
}
export function getData(): DataPoint[] {
const startDate = new Date("2024-01-01");
const data: DataPoint[] = [];
for (let i = 0; i < 50; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
data.push({
date,
price: 100 + Math.sin(i / 5) * 20 + random() * 10,
});
}
return data;
}
export function getNextDataPoint(currentData: DataPoint[]): DataPoint {
const lastPoint = currentData[currentData.length - 1];
const nextDate = new Date(lastPoint.date);
nextDate.setDate(nextDate.getDate() + 1);
return {
date: nextDate,
price: lastPoint.price + (random() - 0.5) * 10,
};
}
{
zoom: {
enabled: true,
onDataChange: {
strategy: 'preserveDomain',
},
},
}In this example:
- The chart is initially zoomed to the middle of the data.
- Start the updates to see new data appended at the end.
- Notice in the Navigator that new data has been added, but the chart view remains fixed on the same time range.
Preserve Ratios Copy Link
strategy: 'preserveRatios', preserves the same zoom percentages regardless of data changes.
This is useful for maintaining a consistent proportional view of your data, with more data points becoming visible as the dataset grows.
import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AgChartsInstance,
LineSeriesModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData, getNextDataPoint } from "./data";
import clone from "clone";
let data = getData();
let streamingInterval: ReturnType<typeof setInterval> | null = null;
ModuleRegistry.registerModules([
LineSeriesModule,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
]);
const ChartExample = () => {
const chartRef = useRef<AgChartsInstance>(null);
const [options, setOptions] = useState<AgCartesianChartOptions>({
data,
series: [
{
type: "line",
xKey: "date",
yKey: "price",
marker: { enabled: false },
},
],
axes: {
x: {
type: "time",
nice: false,
},
y: {
type: "number",
title: { text: "Price" },
},
},
zoom: {
enabled: true,
onDataChange: {
strategy: "preserveRatios",
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
initialState: {
zoom: {
ratioX: { start: 0.8, end: 1 },
},
},
});
const startUpdates = () => {
if (streamingInterval) return;
streamingInterval = setInterval(() => {
const newPoints = [];
for (let i = 0; i < 10; i++) {
const nextPoint = getNextDataPoint(data);
data.push(nextPoint);
newPoints.push(nextPoint);
}
chartRef.current!.applyTransaction({ add: newPoints });
}, 200);
};
const stopUpdates = () => {
if (streamingInterval) {
clearInterval(streamingInterval);
streamingInterval = null;
}
};
const resetData = () => {
const nextOptions = clone(options);
stopUpdates();
data = getData();
nextOptions.data = data;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={startUpdates}>Start Updates</button>
<button onClick={stopUpdates}>Stop Updates</button>
<button onClick={resetData}>Reset</button>
</div>
</div>
<AgCharts ref={chartRef} options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef;
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
const random = seedRandom(12345);
export interface DataPoint {
date: Date;
price: number;
}
export function getData(): DataPoint[] {
const startDate = new Date("2024-01-01");
const data: DataPoint[] = [];
for (let i = 0; i < 50; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
data.push({
date,
price: 100 + Math.sin(i / 5) * 20 + random() * 10,
});
}
return data;
}
let lastPrice = 0;
export function getNextDataPoint(currentData: DataPoint[]): DataPoint {
const lastPoint = currentData[currentData.length - 1];
const nextDate = new Date(lastPoint.date);
nextDate.setDate(nextDate.getDate() + 1);
lastPrice = lastPoint.price + (random() - 0.5) * 10;
return {
date: nextDate,
price: lastPrice,
};
}
{
zoom: {
enabled: true,
onDataChange: {
strategy: 'preserveRatios',
},
},
}In this example:
- The chart is initially zoomed to the last 20% of the data.
- Start the updates to see new data points added in batches.
- Notice that more data points become visible in the chart view as total data grows, since the zoom ratio stays at 80%-100%.
Reset Copy Link
strategy: 'reset', resets the zoom to the initialState or no zoom, whenever the data changes.
This is useful for switching between unrelated datasets, discarding previous user interactions and reverting to a predefined starting view.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
LineSeriesModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getDatasetA, getDatasetB } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
LineSeriesModule,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
data: getDatasetA(),
series: [
{
type: "line",
xKey: "date",
yKey: "price",
marker: { enabled: false },
},
],
axes: {
x: {
type: "time",
nice: false,
},
y: {
type: "number",
title: { text: "Price" },
},
},
zoom: {
enabled: true,
onDataChange: {
strategy: "reset",
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
initialState: {
zoom: {
ratioX: { start: 0.9, end: 1 },
},
},
});
const loadDatasetA = () => {
const nextOptions = clone(options);
nextOptions.data = getDatasetA();
setOptions(nextOptions);
};
const loadDatasetB = () => {
const nextOptions = clone(options);
nextOptions.data = getDatasetB();
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={loadDatasetA}>Load Dataset A</button>
<button onClick={loadDatasetB}>Load Dataset B</button>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef;
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
export interface DataPoint {
date: Date;
price: number;
}
export function getDatasetA(): DataPoint[] {
const random = seedRandom(11111);
const startDate = new Date("2024-01-01");
const data: DataPoint[] = [];
for (let i = 0; i < 60; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
data.push({
date,
price: 100 + Math.sin(i / 5) * 20 + random() * 10,
});
}
return data;
}
export function getDatasetB(): DataPoint[] {
const random = seedRandom(22222);
const startDate = new Date("2024-03-01");
const data: DataPoint[] = [];
for (let i = 0; i < 45; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
data.push({
date,
price: 150 + Math.cos(i / 4) * 30 + random() * 15,
});
}
return data;
}
{
zoom: {
enabled: true,
onDataChange: {
strategy: 'reset',
},
},
}In this example:
- The chart is initially zoomed to show only the last few data points.
- Pan or zoom to a different position in the chart.
- Click a button to switch datasets and notice that the zoom resets to the initial state.
Stick to End Copy Link
The stickToEnd option automatically scrolls to keep the latest data visible when new data is appended and the view is already at the end of the data range. Use stickToEnd: true to enable.
When stickToEnd is active, it takes precedence over the configured strategy. Once the user pans away from the end, the configured strategy (e.g., preserveDomain) takes over until they return to viewing the end.
import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
AgChartsInstance,
LineSeriesModule,
ModuleRegistry,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
} from "ag-charts-enterprise";
import { getData, getNextDataPoint } from "./data";
import clone from "clone";
let data = getData();
let streamingInterval: ReturnType<typeof setInterval> | null = null;
ModuleRegistry.registerModules([
LineSeriesModule,
NavigatorModule,
NumberAxisModule,
TimeAxisModule,
ZoomModule,
]);
const ChartExample = () => {
const chartRef = useRef<AgChartsInstance>(null);
const [options, setOptions] = useState<AgCartesianChartOptions>({
data,
series: [
{
type: "line",
xKey: "date",
yKey: "price",
marker: { enabled: false },
},
],
axes: {
x: {
type: "time",
nice: false,
},
y: {
type: "number",
title: { text: "Price" },
},
},
zoom: {
enabled: true,
onDataChange: {
strategy: "preserveDomain",
stickToEnd: true,
},
},
navigator: {
enabled: true,
miniChart: {
enabled: true,
},
},
initialState: {
zoom: {
ratioX: { start: 0.6, end: 1 },
},
},
});
const startUpdates = () => {
if (streamingInterval) return;
streamingInterval = setInterval(() => {
const nextPoint = getNextDataPoint(data);
data.push(nextPoint);
chartRef.current!.applyTransaction({ add: [nextPoint] });
}, 100);
};
const stopUpdates = () => {
if (streamingInterval) {
clearInterval(streamingInterval);
streamingInterval = null;
}
};
const resetData = () => {
const nextOptions = clone(options);
stopUpdates();
data = getData();
nextOptions.data = data;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<button onClick={startUpdates}>Start Updates</button>
<button onClick={stopUpdates}>Stop Updates</button>
<button onClick={resetData}>Reset</button>
</div>
</div>
<AgCharts ref={chartRef} options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
function sfc32(a: number, b: number, c: number, d: number) {
return function () {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}
function seedRandom(seed = 1337): () => number {
const realSeed = seed ^ 0xdeadbeef;
return sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, realSeed);
}
const random = seedRandom(12345);
export interface DataPoint {
date: Date;
price: number;
}
export function getData(): DataPoint[] {
const startDate = new Date("2024-01-01");
const data: DataPoint[] = [];
for (let i = 0; i < 50; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
data.push({
date,
price: 100 + Math.sin(i / 5) * 20 + random() * 10,
});
}
return data;
}
export function getNextDataPoint(currentData: DataPoint[]): DataPoint {
const lastPoint = currentData[currentData.length - 1];
const nextDate = new Date(lastPoint.date);
nextDate.setDate(nextDate.getDate() + 1);
return {
date: nextDate,
price: lastPoint.price + (random() - 0.5) * 10,
};
}
{
zoom: {
enabled: true,
onDataChange: {
strategy: 'preserveDomain',
stickToEnd: true,
},
},
}In this example:
- The chart is initially zoomed to the end of the data.
- Start the updates to see new data appended and the view scrolls to follow.
- Pan away from the end to see the
preserveDomainstrategy take over with a fixed view. - Pan back to the end and the view will resume following new data.
Navigator Copy Link
The zoom functionality can be used together with the Navigator to add a visual reference to the zoom position.
Context Menu Copy Link
When both the zoom and Context Menu are enabled, additional zoom actions are added into the Context Menu for zooming and panning to the clicked location.
Buttons Copy Link
Zoom buttons are enabled by default. To disable, use zoom.buttons.enabled: false.
Hover near the bottom of the above example to see the default zoom buttons.
- Zoom out: Zooms the chart out by one step.
- Zoom in: Zooms the chart in by one step.
- Pan left: Pans the chart to the left by one step.
- Pan right: Pans the chart to the right by one step.
- Reset: Resets the zoom to the original level and position. Equivalent to Double-Click to Reset.
To change when the buttons will appear, use zoom.buttons.visible:
always– The buttons will always be visible.zoomed– The buttons will appear when the chart has been zoomed.hover– The buttons will appear when the mouse is hovered near the bottom of a chart which has zoom enabled.
Customisation Copy Link
It is possible to customise the visibility, order and grouping of buttons, as well as modifying the icon, label text and tooltip for each.
{
zoom: {
buttons: {
visible: 'always',
buttons: [
{
icon: 'zoom-in',
tooltip: 'Decrease Visible Range',
value: 'zoom-in',
label: 'In',
section: 'zoom',
},
{
icon: 'zoom-out',
tooltip: 'Increase Visible Range',
value: 'zoom-out',
label: 'Out',
section: 'zoom',
},
{
icon: 'pan-start',
tooltip: 'Pan to Start',
value: 'pan-start',
section: 'pan',
},
{
icon: 'pan-end',
tooltip: 'Pan to End',
value: 'pan-end',
section: 'pan',
},
{
tooltip: 'Undo all Zoom',
value: 'reset',
label: 'Reset',
section: 'reset',
},
],
},
},
}In the above example:
- The pan-left and pan-right buttons are not shown.
- Additional buttons are added to enable panning to the start and end of the x-axis.
- All the buttons have custom tooltip text.
- The order of the zoom-in and zoom-out buttons is swapped and they have a label as well as an icon.
- The reset button has only a label and no icon.
For more information see the API Reference section.
Asynchronous Loading Copy Link
For loading data asynchronously as the user zooms and pans, see Asynchronous Data.
Save & Restore Copy Link
The zoom state can be saved, restored and programmatically initialised and modified, using the Chart State API.