LineChart
Lines over categories with dots, curves, labels and a crosshair tooltip.
Default
A natural curve, no dots, a tooltip with no heading and no cursor.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} config={{ desktop: { label: "Desktop", color: "var(--chart-1)" } }}
x="month" curve="natural" margin={{ left: 12, right: 12 }}
categoryAxis={{ tickFormatter: (v) => String(v).slice(0, 3) }}
tooltip={{ hideLabel: true, cursor: false }} />Usage
import { LineChart } from "@elements/ui/chart-line";
import type { ChartConfig } from "@elements/ui/chart";
const config: ChartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" },
};
<LineChart data={rows} {config} x="month" curve="natural" />
<LineChart data={rows} {config} x="month" dots legend labels />Examples
Linear
curve=linear joins the points with straight segments.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} curve="linear" ... />Step
curve=step holds each value to the midpoint; stepBefore and stepAfter hold it before or after.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} curve="step" ... />Multiple
Every series in the config, one line each.
January - June 2024
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<LineChart data={rows} {config} x="month" curve="monotone"
margin={{ left: 12, right: 12 }} tooltip={{ cursor: false }} />Dots
A dot on every point and a larger active dot on hover, each ringed in the surface color.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} curve="natural"
dots activeDot={{ r: 6 }} tooltip={{ hideLabel: true, cursor: false }} />Custom dots
dots shape=commit draws a ring on a stem, a custom dot.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} curve="natural"
dots={{ shape: "commit", r: 4 }} activeDot={{ shape: "commit", r: 5 }} ... />Dots colors
colorByRow fills each dot from its row's fill.
January - June 2024
| Browser | Visitors |
|---|---|
| Chrome | 275 |
| Safari | 200 |
| Firefox | 187 |
| Edge | 173 |
| Other | 90 |
Code
<!-- each row's fill colors its dot -->
<LineChart data={browsers} config={browserConfig} x="browser" series={["visitors"]}
curve="natural" categoryAxis={false} margin={{ top: 24, left: 24, right: 24 }}
dots={{ r: 5, colorByRow: true }} activeDot={{ r: 6, colorByRow: true }}
tooltip={{ indicator: "line", nameKey: "visitors", hideLabel: true, cursor: false }} />Label
A value above every point.
January - June 2024
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} curve="natural"
margin={{ top: 20, left: 12, right: 12 }} dots activeDot={{ r: 6 }}
labels={{ position: "top", offset: 12 }} tooltip={{ indicator: "line", cursor: false }} />Custom label
Labels from another field, formatted through the config.
January - June 2024
| Browser | Visitors |
|---|---|
| Chrome | 275 |
| Safari | 200 |
| Firefox | 187 |
| Edge | 173 |
| Other | 90 |
Code
<LineChart data={browsers} config={browserConfig} x="browser" series={["visitors"]} curve="natural"
categoryAxis={false} margin={{ top: 24, left: 24, right: 24 }}
dots activeDot={{ r: 6 }}
labels={{ key: "browser", offset: 12, formatter: (v) => browserConfig[String(v)].label }}
tooltip={{ indicator: "line", nameKey: "visitors", hideLabel: true, cursor: false }} />Interactive
The header switches the series; 91 points with skipping ticks and a crosshair cursor.
Showing total visitors for the last 3 months
| Date | Desktop |
|---|---|
| 2024-04-01 | 286 |
| 2024-04-02 | 136 |
| 2024-04-03 | 267 |
| 2024-04-04 | 121 |
| 2024-04-05 | 326 |
| 2024-04-06 | 204 |
| 2024-04-07 | 353 |
| 2024-04-08 | 289 |
| 2024-04-09 | 359 |
| 2024-04-10 | 314 |
| 2024-04-11 | 160 |
| 2024-04-12 | 466 |
| 2024-04-13 | 138 |
| 2024-04-14 | 472 |
| 2024-04-15 | 258 |
| 2024-04-16 | 359 |
| 2024-04-17 | 158 |
| 2024-04-18 | 268 |
| 2024-04-19 | 309 |
| 2024-04-20 | 151 |
| 2024-04-21 | 231 |
| 2024-04-22 | 122 |
| 2024-04-23 | 185 |
| 2024-04-24 | 278 |
| 2024-04-25 | 331 |
| 2024-04-26 | 394 |
| 2024-04-27 | 144 |
| 2024-04-28 | 270 |
| 2024-04-29 | 109 |
| 2024-04-30 | 362 |
| 2024-05-01 | 472 |
| 2024-05-02 | 265 |
| 2024-05-03 | 266 |
| 2024-05-04 | 270 |
| 2024-05-05 | 196 |
| 2024-05-06 | 387 |
| 2024-05-07 | 198 |
| 2024-05-08 | 433 |
| 2024-05-09 | 461 |
| 2024-05-10 | 389 |
| 2024-05-11 | 312 |
| 2024-05-12 | 219 |
| 2024-05-13 | 176 |
| 2024-05-14 | 364 |
| 2024-05-15 | 197 |
| 2024-05-16 | 93 |
| 2024-05-17 | 324 |
| 2024-05-18 | 126 |
| 2024-05-19 | 273 |
| 2024-05-20 | 325 |
| 2024-05-21 | 140 |
| 2024-05-22 | 449 |
| 2024-05-23 | 100 |
| 2024-05-24 | 373 |
| 2024-05-25 | 354 |
| 2024-05-26 | 182 |
| 2024-05-27 | 157 |
| 2024-05-28 | 123 |
| 2024-05-29 | 305 |
| 2024-05-30 | 427 |
| 2024-05-31 | 288 |
| 2024-06-01 | 202 |
| 2024-06-02 | 116 |
| 2024-06-03 | 182 |
| 2024-06-04 | 406 |
| 2024-06-05 | 156 |
| 2024-06-06 | 340 |
| 2024-06-07 | 251 |
| 2024-06-08 | 281 |
| 2024-06-09 | 93 |
| 2024-06-10 | 271 |
| 2024-06-11 | 225 |
| 2024-06-12 | 377 |
| 2024-06-13 | 197 |
| 2024-06-14 | 121 |
| 2024-06-15 | 123 |
| 2024-06-16 | 165 |
| 2024-06-17 | 102 |
| 2024-06-18 | 266 |
| 2024-06-19 | 306 |
| 2024-06-20 | 301 |
| 2024-06-21 | 138 |
| 2024-06-22 | 355 |
| 2024-06-23 | 453 |
| 2024-06-24 | 273 |
| 2024-06-25 | 444 |
| 2024-06-26 | 289 |
| 2024-06-27 | 405 |
| 2024-06-28 | 308 |
| 2024-06-29 | 344 |
| 2024-06-30 | 266 |
Code
<LineChart data={daily} config={dailyConfig} x="date" series={[state.series]}
class="chart-demo-wide" curve="monotone" margin={{ left: 12, right: 12 }}
categoryAxis={{ tickFormatter: shortDate, minTickGap: 32 }}
tooltip={{ nameKey: "views", labelFormatter: (_, row) => longDate(row.date) }} />Legend, value axis and toggles
A legend whose items hide and show their line; a value axis with four ticks.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<LineChart data={rows} {config} x="month" curve="monotone" dots
legend={{ toggle: true }} hidden={state.hidden}
valueAxis={{ tickCount: 4 }} />Missing values
A null breaks the line; connectNulls draws across it. The tooltip leaves out a series with no value, and a month with none shows no tooltip.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 120 | |
| April | 73 | 190 |
| May | 209 | |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 120 | |
| April | ||
| May | 209 | |
| June | 214 | 140 |
Code
<!-- null values break a line unless connectNulls -->
<LineChart data={rowsWithNulls} {config} x="month" dots />
<LineChart data={rowsWithNulls} {config} x="month" dots connectNulls />Controlled and keyboard
Tab to the chart; arrows move the crosshair, Home and End jump, Escape hides the tooltip.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<LineChart data={rows} {config} x="month" active={state.active}
onActiveChange={() => state.changes++} strokeWidth={3}
onItemClick={(i) => state.clicked = i} />Synced charts
Two charts with one syncId share the active month and its tooltip.
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
| Month | Mobile |
|---|---|
| January | 80 |
| February | 200 |
| March | 120 |
| April | 190 |
| May | 130 |
| June | 140 |
Code
<!-- the same syncId: hovering or keying one chart shows the same month in the other -->
<LineChart data={rows} {config} x="month" series={["desktop"]} syncId="traffic" />
<LineChart data={rows} {config} x="month" series={["mobile"]} syncId="traffic" />Two value axes
Desktop on the left axis, mobile on its own axis on the right, each scaled to its data.
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- mobile on a second value axis, ticked on the right -->
<LineChart data={rows} {config} x="month"
valueAxis={[
{ domain: [0, "dataMax"] },
{ orientation: "right", series: ["mobile"], domain: [0, "dataMax"] },
]} />Vertical layout
Months run down the chart and values across it; the arrow keys follow them up and down.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- categories down the chart, values across -->
<LineChart data={rows} {config} x="month" layout="vertical" valueAxis dots />Legend type per series
legendType on a config entry sets that series' legend icon; none leaves a drawn series out of the legend.
- Desktop
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- desktop's legend icon is a line; mobile is drawn but not in the legend -->
<LineChart data={rows} x="month" legend config={{
desktop: { label: "Desktop", color: "var(--chart-1)", legendType: "line" },
mobile: { label: "Mobile", color: "var(--chart-2)", legendType: "none" },
}} />Animation timing
animation sets when the marks start moving, how long they take and their easing: a CSS easing name, a cubic-bezier, spring, or a function of progress.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- wait 150ms, then take 900ms to enter or move to new data -->
<LineChart data={rows} {config} x="month"
animation={{ begin: 150, duration: 900, easing: "linear" }} />Legend and tooltip types
legendType on a config entry sets that series' legend icon, and none leaves a drawn series out of the legend; tooltipType none leaves it out of the tooltip.
- Desktop
- Mobile
| Month | Desktop | Mobile | Other |
|---|---|---|---|
| January | 186 | 80 | 45 |
| February | 305 | 200 | 100 |
| March | 237 | 120 | 150 |
| April | 73 | 190 | 50 |
| May | 209 | 130 | 100 |
| June | 214 | 140 | 160 |
Code
<!-- desktop's legend icon is a line; mobile is left out of the tooltip, other out of the legend -->
<LineChart data={rows} x="month" legend config={{
desktop: { label: "Desktop", color: "var(--chart-1)", legendType: "line" },
mobile: { label: "Mobile", color: "var(--chart-2)", tooltipType: "none" },
other: { label: "Other", color: "var(--chart-3)", legendType: "none" },
}} />Dashed line
strokeDasharray on a config entry dashes that series' line; the others stay solid.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- strokeDasharray on a config entry dashes that line -->
<LineChart data={rows} x="month" config={{
desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)", strokeDasharray: "5 5" },
}} />Data table
The data as a table; showTable shows it.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<LineChart data={rows} {config} x="month" showTable={state.table} />Axis padding, log scale and a numeric axis
padding insets the first and last points; a log value axis spaces powers of ten evenly; a numeric category axis places each point by its value.
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
| X | Value |
|---|---|
| a | 1 |
| b | 10 |
| c | 100 |
| d | 1,000 |
| T | Value |
|---|---|
| 0 | 5 |
| 1 | 8 |
| 2 | 4 |
| 10 | 9 |
Code
<LineChart data={rows} {config} x="month" series={["desktop"]} dots
categoryAxis={{ padding: { left: 24, right: 24 } }} />
<LineChart data={powers} config={valueConfig} x="x" series={["v"]} dots
valueAxis={{ scale: "log", domain: ["auto", "auto"] }} />
<LineChart data={timed} config={valueConfig} x="t" series={["v"]} dots
categoryAxis={{ type: "number" }} />Moving to new data
animation matchBy pairs old and new items by a key, so each month slides to its new place; interpolate draws every frame with a function of your own, here one that starts each point after the one before.
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
Code
<!-- each month keeps its point: the window slides instead of every point changing value -->
<LineChart data={rows} {config} x="month" animation={{ matchBy: matchByDataKey("month") }} />
<!-- (items, t) => the items to draw: each matched item from prev to next, started one after another -->
<LineChart data={rows} {config} x="month" animation={{ interpolate: stagger }} />