X Data

Star on GitHubFeedback
X DataNative HTML and a hookot-line-chart

Line chart

One hairline through the points, a ring on each, against one very large number that rolls to the point you're on.

Report an issue with Line chart

Example

4,210visits in September
September4,21020262,9402025
4,210visits in September
Rest
4,210visits in September
Pointed at
4,210visits in September
smooth
4,210visits in September
step
4,210visits in September
two series, pointed at
September4,21020262,9402025

Install

Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and chart along.

npx shadcn@latest add https://thedirectors.agency/ui/r/line-chart.json

Usage

line-chart-example.tsx
import { LineChart } from "@/components/ui/line-chart"const visits = [{ label: "January", value: 1820 },{ label: "February", value: 2140 },{ label: "March", value: 2600 },{ label: "April", value: 2380 },{ label: "May", value: 3050 },{ label: "June", value: 2890 },{ label: "July", value: 3400 },{ label: "August", value: 3120 },{ label: "September", value: 4210 },]// This year against last, month by month.const years = visits.map((d, i) => ({ label: d.label, now: d.value, then: [1540, 1610, 1990, 2210, 2050, 2480, 2300, 2720, 2940][i] }))const both = [{ key: "now", label: "2026" },{ key: "then", label: "2025" },]export default function Example() {return (<div className="grid gap-y-16"><LineChart data={visits} label="Visits by month, 2026" unit="visits" className="max-w-[44rem]" /><LineChart variant="smooth" data={years} series={both} label="Visits by month, 2026 against 2025" className="max-w-[44rem]" /><LineChart variant="step" data={visits} label="Visits by month, 2026" unit="visits" className="max-w-[44rem]" /></div>)}

Contract

  • Underneath: chart's frame (ChartFrame), plus a hook. LineChart and AreaChart share ChartLines, which lays an SVG under chart's points.
  • Anatomy: <figure class="ot-chart ot-line-chart" data-variant="linear | smooth | step"> holding chart's .ot-chart-read, then .ot-chart-plot with <svg class="ot-line-chart-draw" aria-hidden> (a <g data-series> per series, each a .ot-line-chart-line) under chart's <button class="ot-chart-bar"> per point, then .ot-chart-axis. Several series add chart's .ot-chart-marks and list readout.
  • POINT and SPECTRA: tiny data against one giant number. One ink hairline runs through the points, drawn in pixels (measured with a ResizeObserver) so it stays a hairline at every width, and a paper ring sits on each point like a bead on a thread; now's ring is the one accent. The number above is chart's, as large as the chart allows. Pointing at a column, or focusing it, swells its ring, inks it, and drops the bar chart's own stem from the ring to the baseline (moderato, exhale), as if the line were the tops of the stems with the stems taken away; the number rolls to it and back to now when you leave. Before the plot is measured, only the rings show.
  • Series: the second line is dotted graphite and the third dashed pencil; the readout becomes chart's short list, named roman, italic and pencil. Lines are not stacked.
  • linear (the default): straight from point to point.
  • smooth: the sweep of "Less stress. More creativity."'s script through its capitals, made honest: a monotone cubic (Fritsch and Carlson) through every point that never overshoots one, so the curve never claims a value the data doesn't have.
  • step: Paul Rand's dimension lines, right angles only. Each point holds level across its own column and the line stands upright between columns, the draughtsman's line.
  • Keyboard: chart's. Right to left the drawing is mirrored with the columns.

Props

Props of Line chart
PropTypeDefaultWhat it does
data{ label: string; value?: number; [key: string]: string | number | undefined }[]One point per entry, in order. With one series the number is value; with several, each series reads its own key. The first letter of each label sets the axis.
series{ key: string; label: string }[]Two or more lines. The first is a solid ink hairline, the second dotted graphite, the third dashed pencil; the one giant number becomes a short list of each at the point you're on, named roman, italic and pencil. Written for up to three.
labelstringNames the chart for assistive tech, such as Visits by month, 2026. Each point is a button labelled with its values.
unitstringWhat the values count. The readout reads visits in September.
nownumberlastIndex of the point the number rests on. Its ring is the accent.
maxnumberThe value at the top of the plot. Defaults to the next round number above the largest.
format(value: number) => stringFormats the numbers and the point labels. Defaults to en-GB grouping.
variant"linear" | "smooth" | "step""linear"The path between points. linear: straight, point to point. smooth: one sweep through every point that never overshoots one (monotone), so the curve never claims a value the data doesn't have. step: level across each point's column and upright between them, the draughtsman's line.
ChartLines areabooleanfalseThe exported shared drawing body can stack series and draw the hatched drops beneath each line. Use AreaChart for the complete styled area chart.

Craft

Where its move comes from

Where its move comes from
ComponentReferenceMove
Line chartPOINT / SPECTRAOne hairline with a ring per point; pointing drops the stem it stands for
Smooth line"Less stress. More creativity." scriptOne monotone sweep through the points, never past them
Step linePaul Rand's dimension linesRight angles only, level across each column

How it moves

How it moves
ComponentArticulationWhat moves
Line chartRollThe ring swells, a drop runs down from it to the baseline (moderato, exhale) and the number rolls; leaving, the drop withdraws