Chartist

Search

Find a guide, an API entry or an example.

Try

Axis titles

Name an axis on any side of the chart. The title reserves its own room, so it never overlaps the labels.

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.

src/main.ts
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' })
      ]
    }
  }
);
Live

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:

src/main.ts
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 })
  ]
});
Live

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.