Line chart
A multi-series line chart with a gliding crosshair, legend toggles, and paths that morph between ranges.
pnpm dlx shadcn@latest add @uiarc/line-chartLive · keyboard ready
Trial signups
Total signups6,670
+20.6% vs previous 30 days
Trial signups, Sun, Aug 23 to Today, Sep 21. This period, latest 248. Previous period, latest 183.
| Date | This period | Previous period |
|---|---|---|
| Sun, Aug 23 | 128 | 121 |
| Mon, Aug 24 | 198 | 202 |
| Tue, Aug 25 | 192 | 198 |
| Wed, Aug 26 | 211 | 190 |
| Thu, Aug 27 | 206 | 182 |
| Fri, Aug 28 | 199 | 172 |
| Sat, Aug 29 | 130 | 121 |
| Sun, Aug 30 | 159 | 134 |
| Mon, Aug 31 | 254 | 218 |
| Tue, Sep 1 | 262 | 217 |
| Wed, Sep 2 | 247 | 226 |
| Thu, Sep 3 | 260 | 223 |
| Fri, Sep 4 | 253 | 231 |
| Sat, Sep 5 | 169 | 146 |
| Sun, Sep 6 | 158 | 121 |
| Mon, Sep 7 | 231 | 184 |
| Tue, Sep 8 | 239 | 203 |
| Wed, Sep 9 | 437 | 200 |
| Thu, Sep 10 | 338 | 181 |
| Fri, Sep 11 | 240 | 187 |
| Sat, Sep 12 | 139 | 118 |
| Sun, Sep 13 | 134 | 148 |
| Mon, Sep 14 | 250 | 235 |
| Tue, Sep 15 | 268 | 211 |
| Wed, Sep 16 | 262 | 230 |
| Thu, Sep 17 | 260 | 226 |
| Fri, Sep 18 | 268 | 232 |
| Sat, Sep 19 | 154 | 143 |
| Sun, Sep 20 | 176 | 147 |
| Today, Sep 21 | 248 | 183 |
- Traffic, revenue, or latency over days or weeks in a dashboard card.
- Comparing this period against a previous period or target with a dashed series.
- Charts that switch ranges (7d, 30d, 90d) and should morph rather than redraw.
- Use sparkline for a tiny inline trend with no axes or tooltip.
- Use bar-chart for discrete categories or counts per bucket.
- Use donut-chart when the question is the share of a whole, not change over time.
Installation
Add Line chart with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/line-chartAdds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:
example.tsx
import { LineChart } from "@/registry/components/line-chart/line-chart"; const data = [ { key: "2026-09-01", label: "Tue, Sep 1", axisLabel: "Sep 1", values: { signups: 120, previous: 98 } }, { key: "2026-09-02", label: "Wed, Sep 2", values: { signups: 142, previous: 104 } }, { key: "2026-09-03", label: "Thu, Sep 3", axisLabel: "Sep 3", values: { signups: 131, previous: 110 } },]; export function SignupsChart() { return ( <LineChart label="Signups" data={data} series={[ { key: "signups", label: "This period" }, { key: "previous", label: "Previous period", dashed: true }, ]} /> );}Headline readout driven by the crosshair
example.tsx
const [active, setActive] = useState<LineChartDatum | null>(null); <p>{(active ?? data[data.length - 1]).values.signups}</p><LineChart label="Signups" data={data} series={series} onActiveChange={(_, datum) => setActive(datum)} />LineChart
An SVG line chart with a scrubbable crosshair, series toggles, morphing data changes, and a screen reader data table.
PropTypeDefaultDescription
dataRequiredLineChartDatum[]–Points: { key, label, axisLabel?, values }. values maps series keys to numbers; a missing value counts as zero.seriesRequiredLineChartSeries[]–Lines: { key, label, color?, dashed?, area? }. area defaults to true for the first series.labelRequiredstring–What is measured, such as "Signups". Names the chart for assistive technology.unitstring""Unit after each value in the tooltip and table, such as "ms".heightnumber220Plot height in pixels. The width follows the container.formatValue(value: number, series: LineChartSeries) => string–Formats values in the tooltip and data table. Defaults to grouped numbers with one decimal.formatTick(value: number) => stringcompact formatFormats the value axis, such as 1.2K.hiddenSeriesstring[]–Controlled hidden series keys.defaultHiddenSeriesstring[]–Hidden series keys on first render when uncontrolled.onHiddenSeriesChange(hidden: string[]) => void–Called when a legend toggle changes the hidden keys.onActiveChange(index: number | null, datum: LineChartDatum | null) => void–Called as the crosshair moves, and with null when it leaves. Use it to drive a headline readout.loadingbooleanfalseDims the current lines while the next range loads, or shows a skeleton when there is no data.emptyLabelstring"No data for this range"Shown when there are no points.legendboolean–Series toggles above the plot. Shown by default when there is more than one series.categoryLabelstring"Date"Header of the first column in the screen reader table, and part of the plot's slider label.curve"smooth" | "linear""smooth"Monotone curves that never swing past the data, or straight segments.refRef<HTMLElement>–The figure element.classNamestring–Class on the figure.- ArrowLeftorArrowDown
- Moves the crosshair to the previous point.
- ArrowRightorArrowUp
- Moves to the next point; from rest it starts on the latest point.
- PageUporPageDown
- Jumps about a sixth of the range.
- HomeorEnd
- Jumps to the first or last point.
- Escape
- Lets go of the crosshair.
- The figure is named by label; the plot is a role="slider" whose aria-valuetext reads the date and every visible series value.
- A visually hidden summary gives the range and latest values, and a hidden table lists every point for screen readers.
- Legend toggles are buttons with aria-pressed; a hidden live region says Loading while loading is true.
- The SVG and tooltip are aria-hidden, so values are never read twice.
- Lines draw left to right the first time the chart scrolls into view.
- New data morphs every line from the shape on screen, sampled on shared x positions, and the value scale springs to its new range while gridlines slide.
- A hidden series flattens into the baseline as it fades; the crosshair glides between points and the tooltip follows on its own spring, flipping sides near the edge.
- Reduced motion skips the draw, morphs, and glides, and stops the loading pulse.
- Width follows the container through a ResizeObserver; the plot redraws in its own pixels, and axis labels thin to one per 76px.
- Touch scrubs with pointer capture and releases on lift; touch-action: pan-y keeps vertical page scroll working over the plot.
- A mouse scrubs on hover and releases on leave. The value gutter is a fixed 48px column.
- Paths are written straight to the DOM from motion values, not React state, so scrubbing and morphs do not re-render the tree.
- Smooth curves are sampled up to 16 points per segment (about 192 per line); keep data to a few hundred points per series.
- One ResizeObserver per chart; the draw-in waits for useInView.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use for one to three measures over time when the trend shape matters. For category comparison use bar-chart; for parts of a whole use donut-chart.
- Keep series and datum keys stable across range changes so lines morph instead of redrawing.
- Set axisLabel only on the dates you want on the axis; the chart thins them further to one per 76px, always keeping the newest.
- Drive a headline number from onActiveChange, as in metric-card or stat-card layouts.
The full library index for assistants is at /llms.txt.