Skip to content

Line Chart

The Line Chart connects each series' points in order along a shared x axis, so the reading is the trend between points rather than the points themselves. It fits anything ordered — time series, sequences, sweeps — and comparing several such series against one another. Each series picks its own lineType from 13 polyline interpolation modes (linear, monotone, cardinal, catmull-rom, step, and more), plus lineWidth, lineStyle, markers and a yAxis to bind to when the series need separate scales. Crosshair tracking, grid lines, a legend and tooltips are built in, and points enter, exit and reposition under configurable transitions. Renders to Canvas, SVG or a terminal context.

NOTE

For the full API, see the Charts API Reference.

Example

Usage

ts
import {
    createLineChart,
} from '@ripl/charts';

const chart = createLineChart('#container', {
    data: [/* ... */],
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            lineType: 'monotoneX',
        },
    ],
});

// Update data
chart.update({ data: newData });

Data Format

Each item should contain a key field and one or more numeric value fields:

ts
const data = [
    {
        month: 'Jan',
        revenue: 620,
        margin: 18,
        units: 2400,
    },
    {
        month: 'Feb',
        revenue: 780,
        margin: 24,
        units: 3100,
    },
    {
        month: 'Mar',
        revenue: 550,
        margin: 11,
        units: 1900,
    },
];

The key option identifies the x-axis category ('month'), and each series references a numeric field via value.

Variants

Multi-series with markers

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            markers: true,
        },
        {
            id: 'units',
            value: 'units',
            label: 'Units',
            markers: true,
        },
    ],
});

Custom line interpolation

Each series can use a different polyline renderer:

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            lineType: 'monotoneX',
        },
        {
            id: 'units',
            value: 'units',
            label: 'Units',
            lineType: 'step',
        },
    ],
});

Segmented line styles

lineStyle also accepts spans anchored to data keys, so one line can change style along its length — actuals solid and a forecast dashed, say. The line is still a single polyline, so the draw-on animation and point morphing are unaffected.

Pass an array of segments and everything they do not cover stays solid:

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            lineStyle: [
                {
                    from: 'Feb',
                    to: 'Jun',
                    style: 'dashed',
                },
                {
                    // A function receives the dataset and returns the key to anchor to.
                    from: data => data[data.length - 3].month,
                    style: 'dotted',
                },
            ],
        },
    ],
});

Or name the fallback explicitly with the object form:

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            lineStyle: {
                default: 'solid',
                segments: [
                    {
                        from: 'Feb',
                        to: 'Jun',
                        style: 'dashed',
                    },
                ],
            },
        },
    ],
});

from defaults to the start of the line and to — which is inclusive — to its end. Segments apply in order, so a later one wins where two overlap, and a segment whose key is not in the data is skipped rather than throwing, leaving that span at the default style.

NOTE

A basis line is a B-spline that passes through none of its points, so there is no point at which to split it: a segmented basis line falls back to a single style. Every other lineType splits exactly on the point you name.

Time x-axis

Set axis.x.scale to 'time' to treat keys as dates: points are positioned continuously by timestamp (unevenly spaced samples sit proportionally to their dates, not evenly), and ticks are calendar-aligned Date values:

ts
createLineChart('#container', {
    data: [
        {
            date: '2024-01-02',
            value: 34,
        },
        {
            date: '2024-01-05',
            value: 41,
        },
        {
            date: '2024-02-19',
            value: 28,
        },
    ],
    key: 'date',
    series: [
        {
            id: 'value',
            value: 'value',
            label: 'Value',
        },
    ],
    axis: {
        x: { scale: 'time' },
    },
});

Marker symbols

Each series can render its markers with a distinct symbol shape ('circle', 'square', 'diamond', or 'triangle'; non-circle symbols are sized to the same visual area as the circle):

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            marker: 'circle',
        },
        {
            id: 'units',
            value: 'units',
            label: 'Units',
            marker: 'diamond',
        },
    ],
});

Multiple y-axes

Supply an array of axis.y entries to render any number of y-axes, and bind each series to one with its yAxis option, naming the axis's id. Every axis scales independently to the extent of the series bound to it, so metrics with very different units and magnitudes stay readable on one plot. Axes with position: 'right' sit on the right of the plot; the rest default to the left, and axes on the same side stack outward in array order:

ts
createLineChart('#container', {
    data,
    key: 'month',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            yAxis: 'revenue',
        },
        {
            id: 'margin',
            value: 'margin',
            label: 'Margin',
            yAxis: 'margin',
        },
        {
            id: 'units',
            value: 'units',
            label: 'Units',
            yAxis: 'units',
        },
    ],
    axis: {
        y: [
            {
                id: 'revenue',
                title: 'Revenue ($)',
            },
            {
                id: 'margin',
                title: 'Margin (%)',
                position: 'right',
            },
            {
                id: 'units',
                title: 'Units',
                position: 'left',
            },
        ],
    },
});

Options

A full configuration for this chart. The options every chart shares — padding, title, animation, theme and the rest — behave the same everywhere and are documented on Shared Options.

ts
createLineChart('#container', {
    data,
    key: 'month',
    labels: true,
    format: 'number',
    series: [
        {
            id: 'revenue',
            value: 'revenue',
            label: 'Revenue',
            color: '#7cacf8',
            lineType: 'monotoneX',
            lineStyle: 'solid',
            lineWidth: 2,
            markers: true,
            marker: 'circle',
            markerRadius: 3,
            yAxis: 'revenue',
        },
        {
            id: 'margin',
            value: 'margin',
            label: 'Margin',
            color: '#6dd5b1',
            marker: 'diamond',
            yAxis: 'margin',
        },
    ],
    axis: {
        y: [
            {
                id: 'revenue',
                title: 'Revenue ($)',
            },
            {
                id: 'margin',
                position: 'right',
                title: 'Margin (%)',
            },
        ],
    },
});

Events

Subscribe with chart.on(...). A handler receives an Event object, not the payload directly — the payload is on event.data, and carries the interacted datum plus its { x, y } anchor in chart pixels. event.target and event.stopPropagation() are also available.

ts
// Emitted when a marker is clicked.
chart.on('markerclick', event => console.log(event.data)); // event.data: LineChartMarkerEvent
// Emitted when the pointer enters a marker.
chart.on('markerenter', event => console.log(event.data)); // event.data: LineChartMarkerEvent
// Emitted when the pointer leaves a marker.
chart.on('markerleave', event => console.log(event.data)); // event.data: LineChartMarkerEvent