Cartesian axes can be positioned on the top, bottom, left, or right edge of the chart.
Axis Placement Copy Link
The axis position property controls where the axis is rendered. By default, horizontal axes appear at the bottom of the chart and vertical axes appear on the left.
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianChartOptions,
BarSeriesModule,
CategoryAxisModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
} from "ag-charts-community";
ModuleRegistry.registerModules([
BarSeriesModule,
CategoryAxisModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
title: {
text: "Company Financials (2023)",
},
data: [
{ quarter: "Q1", revenue: 8.5, profitMargin: 22 },
{ quarter: "Q2", revenue: 11.2, profitMargin: 27 },
{ quarter: "Q3", revenue: 9.8, profitMargin: 25 },
{ quarter: "Q4", revenue: 13.4, profitMargin: 31 },
],
axes: {
x: {
title: { text: "Quarter" },
},
y: {
position: "left",
title: { text: "Revenue ($M)" },
line: {
stroke: "red",
width: 3,
},
},
ySecondary: {
type: "number",
position: "right",
title: { text: "Profit Margin (%)" },
label: {
formatter: ({ value }) => `${value}%`,
},
line: {
stroke: "red",
width: 3,
},
},
},
series: [
{
type: "bar",
xKey: "quarter",
yKey: "revenue",
},
{
type: "line",
xKey: "quarter",
yKey: "profitMargin",
yKeyAxis: "ySecondary",
},
],
});
return <AgCharts options={options} />;
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
{
axes: {
x: {
type: 'category',
position: 'bottom',
title: { text: 'Quarter' },
},
y: {
type: 'number',
position: 'left',
title: { text: 'Revenue ($M)' },
},
ySecondary: {
type: 'number',
position: 'right',
title: { text: 'Profit Margin (%)' },
},
},
}Secondary Axes default to the opposite edge from the primary axis, but it is possible to have multiple axes on the same edge. These are displayed alongside each other.
Axis Crossing Point Copy Link
The crossAt option allows an axis to intersect a perpendicular axis at a specific axis value instead of the chart edge. This is useful for centring the chart origin or aligning axes to highlight particular thresholds.
The axis title, labels and crosshair label can be positioned alongside the axis or at the edge of the chart.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgCartesianAxisCrossAtPlacement,
AgCartesianChartOptions,
CrosshairModule,
LegendModule,
LineSeriesModule,
ModuleRegistry,
NumberAxisModule,
} from "ag-charts-enterprise";
import { getData } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
CrosshairModule,
LegendModule,
LineSeriesModule,
NumberAxisModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions>({
theme: {
overrides: {
common: {
axes: {
number: {
line: {
enabled: true,
stroke: "red",
},
label: {
color: "red",
},
},
},
},
},
},
title: { text: "Axes crossing at 0", fontWeight: "bold" },
data: getData(),
axes: {
x: {
type: "number",
title: { text: "X Axis" },
crossAt: {
value: 0,
},
},
y: {
type: "number",
title: { text: "Y Axis" },
crossAt: {
value: 0,
},
},
},
series: [
{
type: "line",
xKey: "x",
yKey: "y",
yName: "Function plot",
strokeWidth: 3,
marker: { size: 0 },
},
],
});
const setTitlePlacement = (placement: AgCartesianAxisCrossAtPlacement) => {
const nextOptions = clone(options);
nextOptions.axes!.x!.crossAt!.titlePlacement = placement;
nextOptions.axes!.y!.crossAt!.titlePlacement = placement;
setOptions(nextOptions);
};
const setLabelPlacement = (placement: AgCartesianAxisCrossAtPlacement) => {
const nextOptions = clone(options);
nextOptions.axes!.x!.crossAt!.labelPlacement = placement;
nextOptions.axes!.y!.crossAt!.labelPlacement = placement;
setOptions(nextOptions);
};
const setCrosshairLabelPlacement = (
placement: AgCartesianAxisCrossAtPlacement,
) => {
const nextOptions = clone(options);
nextOptions.axes!.x!.crossAt!.crosshairLabelPlacement = placement;
nextOptions.axes!.y!.crossAt!.crosshairLabelPlacement = placement;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<label htmlFor="title-placement-select">Title Placement:</label>
<select
className="gap-right"
id="title-placement-select"
onChange={(event) => setTitlePlacement(event.target.value)}
>
<option value="crossing">crossing</option>
<option value="edge">edge</option>
</select>
<label htmlFor="label-placement-select">Label Placement:</label>
<select
className="gap-right"
id="label-placement-select"
onChange={(event) => setLabelPlacement(event.target.value)}
>
<option value="crossing">crossing</option>
<option value="edge">edge</option>
</select>
<label htmlFor="crosshair-label-placement-select">
Crosshair Label Placement:
</label>
<select
id="crosshair-label-placement-select"
onChange={(event) => setCrosshairLabelPlacement(event.target.value)}
>
<option value="edge">edge</option>
<option value="crossing">crossing</option>
</select>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
type DataType = { x: number; y: number | null }[];
export function getData(): DataType {
const dataNeg: DataType = [];
for (let x = -6; x <= -0.1; x += 0.05) dataNeg.push({ x, y: 1 / x });
const dataPos: DataType = [];
for (let x = 0.1; x <= 6; x += 0.05) dataPos.push({ x, y: 1 / x });
return [...dataNeg, { x: 0, y: null }, ...dataPos];
}
{
axes: {
x: {
type: 'number',
// place the bottom axis at '0' on the left axis scale
crossAt: {
value: 0,
titlePlacement: 'crossing',
labelPlacement: 'crossing',
crosshairLabelPlacement: 'edge',
},
},
y: {
type: 'number',
// place the left axis at '0' on the bottom axis scale
crossAt: {
value: 0,
titlePlacement: 'crossing',
labelPlacement: 'crossing',
crosshairLabelPlacement: 'edge',
},
},
},
}In this example:
- Both axes have a
crossAt.valueset. It is also possible to set it on a single axis only. - The
crossAt.titlePlacement,crossAt.labelPlacementandcrossAt.crosshairLabelPlacementproperties control where these elements are placed. - The
crossAt.valuemust be of the same type as the perpendicular axis domain, such asNumberorDate. - The
crossAttarget value should be within the domain of the perpendicular axis.
When the target value leaves the visible range, such as by Zooming, the axis will stick to the edge of the chart and remain in view. Set sticky: false to allow the axis to be moved out of view in this scenario.
Band Alignment Copy Link
The bandAlignment option controls the band layout when using fixed width bars.
import React, { useState, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
AgBandAlignment,
AgCartesianChartOptions,
AgCategoryAxisOptions,
BarSeriesModule,
CategoryAxisModule,
LegendModule,
ModuleRegistry,
NumberAxisModule,
} from "ag-charts-enterprise";
import { DataType, getData } from "./data";
import clone from "clone";
ModuleRegistry.registerModules([
BarSeriesModule,
CategoryAxisModule,
NumberAxisModule,
LegendModule,
]);
const ChartExample = () => {
const [options, setOptions] = useState<AgCartesianChartOptions<DataType>>({
data: getData(),
title: {
text: "Total Visitors to Museums and Galleries",
},
footnote: {
text: "Source: Department for Digital, Culture, Media & Sport",
},
series: [
{
type: "bar",
xKey: "quarter",
yKey: "museums",
yName: "Museums",
width: 10,
},
{
type: "bar",
xKey: "quarter",
yKey: "galleries",
yName: "Galleries",
width: 10,
},
{
type: "bar",
xKey: "quarter",
yKey: "heritage",
yName: "Heritage Sites",
width: 10,
},
],
axes: {
x: {
type: "category",
bandAlignment: "start",
},
y: {
type: "number",
title: {
text: "Total Visitors (Millions)",
},
},
},
formatter: {
y(params) {
const value = params.value as number;
const millions = value / 1000000;
const accuracy = ["series-label", "axis-label"].includes(params.source)
? 0
: 1;
return `${millions.toFixed(accuracy)}M`;
},
},
});
const alignmentChange = (event: Event) => {
const nextOptions = clone(options);
const alignment = (event.target as HTMLInputElement)
.value as AgBandAlignment;
(nextOptions.axes!.x! as AgCategoryAxisOptions).bandAlignment = alignment;
setOptions(nextOptions);
};
return (
<Fragment>
<div className="example-controls">
<div className="controls-row">
<div
className="button-group"
role="group"
aria-label="Band Alignment"
>
<input
type="radio"
id="alignment-justify"
name="band-alignment"
defaultValue="justify"
onChange={(event) => alignmentChange(event)}
/>
<label htmlFor="alignment-justify">Justify</label>
<input
type="radio"
id="alignment-start"
name="band-alignment"
defaultValue="start"
defaultChecked
onChange={(event) => alignmentChange(event)}
/>
<label htmlFor="alignment-start">Start</label>
<input
type="radio"
id="alignment-center"
name="band-alignment"
defaultValue="center"
onChange={(event) => alignmentChange(event)}
/>
<label htmlFor="alignment-center">Center</label>
<input
type="radio"
id="alignment-end"
name="band-alignment"
defaultValue="end"
onChange={(event) => alignmentChange(event)}
/>
<label htmlFor="alignment-end">End</label>
</div>
</div>
</div>
<AgCharts options={options} />
</Fragment>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);
export interface DataType {
quarter: string;
museums: number;
galleries: number;
heritage: number;
}
export function getData(): DataType[] {
return [
{ quarter: "Q1", museums: 12836720, galleries: 8472190, heritage: 5631280 },
{ quarter: "Q2", museums: 14272922, galleries: 9123450, heritage: 6284130 },
{ quarter: "Q3", museums: 13800193, galleries: 9842310, heritage: 7123540 },
{ quarter: "Q4", museums: 12458355, galleries: 8930240, heritage: 5429240 },
];
}
{
axes: {
x: {
type: 'category',
bandAlignment: 'start',
},
},
}See the Series Bars documentation for more details.