A chart of “1200, 1450, 1380” says little until you know they’re visits. axisTitle() puts a name next to an axis. It measures its text and reserves that much room beside the axis labels, so the drawing area shrinks a little and you don’t have to tune chartPadding until the title stops covering the labels.
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
import { lineChart } from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
// Visits per day, with both axes named. Each title reserves its own room.
lineChart(
'#chart',
{
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
series: [[1200, 1450, 1380, 1620, 1500, 980, 860]]
},
{
low: 0,
layers: {
above: [
axisTitle({ axis: 'x', text: 'Day of the week' }),
axisTitle({ axis: 'y', text: 'Visits' })
]
}
}
);One title per axis
A layer draws one title, for the axis you name with axis. On the presets, the axes are x and y, wherever they sit:
import { lineChart } from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
lineChart(
'#chart',
{ labels: ['Mon', 'Tue', 'Wed'], series: [[12, 9, 7]] },
{
axisX: { position: 'start' },
layers: {
above: [
axisTitle({ axis: 'x', text: 'Day' }),
axisTitle({ axis: 'y', text: 'Visits' })
]
}
}
);
With position: 'start' the X axis is at the top, and so is its title. On horizontal bars, x is the value axis at the bottom. An empty text draws nothing and takes no room, which is handy in responsive options that drop the title on small screens.
Several axes
In a composed chart, use the ids you gave the axes. Each title sits outside its axis’s labels, on the axis’s side:
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
import {
autoScaleAxis,
axisGrid,
axisLabels,
bar,
cartesianAxes,
chart,
line,
stepAxis
} from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
// Revenue in EUR as bars on the left axis, ad spend in USD as a line on the
// right one, and a title for each axis, by its id.
chart('#chart', {
data: {
labels: months,
series: [
{ name: 'revenue', data: [42, 48, 51, 47, 58, 63] },
{ name: 'ads', data: [6.2, 7.1, 6.8, 5.9, 8.4, 9.0] }
]
},
axes: cartesianAxes([
{ id: 'month', side: 'bottom', type: stepAxis() },
{
id: 'eur',
side: 'left',
type: autoScaleAxis(),
referenceValue: 0,
labelTransform: value => `${value}k`
},
{
id: 'usd',
side: 'right',
type: autoScaleAxis(),
low: 0,
labelTransform: value => `${value}k`
}
]),
layers: [
axisGrid({ axes: ['month', 'eur'] }),
bar({ series: ['revenue'], x: 'month', y: 'eur' }),
line({ series: ['ads'], x: 'month', y: 'usd', align: 'center' }),
axisLabels(),
axisTitle({ axis: 'month', text: '2026', align: 'end' }),
axisTitle({ axis: 'eur', text: 'Revenue (EUR)' }),
// Turned to read from top to bottom, toward the chart.
axisTitle({ axis: 'usd', text: 'Ad spend (USD)', flip: true })
]
});Titles on the same side stack outward in the order of layers. With two Y axes on the left, list their titles in the order of the axes, so each title lines up with its own labels.
Placement
| Option | What it does |
|---|---|
align |
start, middle (default) or end along the axis. The start of an X axis is its left, the start of a Y axis its bottom. |
flip |
Left and right titles read from bottom to top; flip: true turns them to read from top to bottom. Titles of top and bottom axes don’t change. |
gap |
The space between the title and the axis labels, in px (4 by default). |
The right axis above uses flip: true, so its title faces the chart, and the year is aligned to the end of the X axis.
Styling
The title is an SVG text with the classes ct-axis-title, ct-horizontal (top and bottom) or ct-vertical (left and right), and ct-start (top and left) or ct-end (bottom and right). It looks like the axis labels until you say otherwise:
.ct-axis-title {
--ct-axis-title-color: #555;
--ct-axis-title-size: 0.875rem;
font-weight: 600;
}
--ct-axis-title-color and --ct-axis-title-size fall back to --ct-label-color and --ct-label-size, so a page that restyles the labels restyles the titles too. The room follows the size: a larger font reserves a taller line. className adds classes (ct-series-b gives a title the color of the second series), and classNames: { title } replaces ct-axis-title.
In compose listeners and plugins, a title has the role axisTitle, with its axis, side, text and position in ctx.
On the server and in the terminal
Titles are SVG text in every label mode, so server rendering draws them, applyStyles() styles them, and hydration takes them over. On the server, the room comes from Chartist’s text estimate. In the browser it comes from the stylesheet’s font; a chart that takes over server markup keeps the server’s room until its next resize, so hydration repairs nothing. @chartist-js/terminal draws them too, vertical titles in a column of their own.
Coming from chartist-plugin-axistitle
The 1.x plugin drew titles in the created event and reserved nothing, so its titles needed an offset and a chartPadding that left room for them. axisTitle() reserves the room itself:
chartist-plugin-axistitle |
axisTitle() |
|---|---|
axisX / axisY |
One layer per axis: axis: 'x' or axis: 'y' |
axisTitle |
text |
axisClass |
classNames: { title } |
textAnchor |
align |
flipTitle |
flip |
offset |
Not needed: the title sits in its own room. gap sets the space to the labels. |
| A function as title | Change the text with update() or responsive options. |
Next: every option on the API page, or how a layer reserves room in Custom layers.