const data = /* ... */;
<LineChart
data={data}
x="month"
y={["desktop", "mobile"]}
labels={{ desktop: "Desktop", mobile: "Mobile" }}
curve="monotone"
ariaLabel="Desktop and mobile visitors, January through June"
/>LineChart answers how a value changes across an ordered domain. Give it rows and the fields to read — it builds the scales, the axes, the legend, the tooltip, and the keyboard navigation for you, all colored from your palette.
Installation
pnpm dlx shadcn@latest add @dotui/chart-lineIt brings the chart core — the host, the palette, and the shared frame — along with it.
Usage
import { LineChart } from "@/components/ui/chart-line"const chartData = [
{ month: "Jan", desktop: 186 },
{ month: "Feb", desktop: 305 },
{ month: "Mar", desktop: 237 },
]
export function Example() {
return (
<LineChart
data={chartData}
x="month"
y="desktop"
labels={{ desktop: "Desktop" }}
ariaLabel="Desktop visitors, January through March"
/>
)
}x and y name fields on the row; labels gives each series a display name for the legend and the tooltip. data is compared by identity, so define it outside your component — or memoize it — and the chart rebuilds only when the rows actually change.
Wide and long data
Two data shapes, one component. Wide rows carry one field per series:
<LineChart
data={rows}
x="month"
y={["desktop", "mobile"]}
ariaLabel="Visitors by month"
/>Long rows repeat the x value and name their series in a field. Set seriesOrder to lead the color slots and the legend — series the data carries but seriesOrder omits follow in the order they first appear:
<LineChart
data={rows}
x="month"
y="visitors"
series="device"
seriesOrder={["desktop", "mobile"]}
ariaLabel="Visitors by month and device"
/>Either way, series are colored in order from --chart-1 through --chart-8.
Curve
curve chooses the path between points: natural (the default), monotone, step, or linear.
<LineChart
data={rows}
x="month"
y="desktop"
curve="step"
ariaLabel="Desktop visitors by month"
/>Interpolation only changes how the line travels between observations — it does not smooth the data or claim anything about the values in between. A null value is a gap, not a zero: the line stops and restarts on the far side.
Annotations
marks paints extra layers over the lines, marksBefore under them. Both take marks straight from @tanstack/charts, so labels, reference rules, and custom dots compose with the same scales:
import { text } from "@tanstack/charts/text"
const labels = text(chartData, {
x: "month",
y: "desktop",
text: "desktop",
z: () => "Desktop",
dy: -12,
})<LineChart
data={chartData}
x="month"
y="desktop"
marks={[labels]}
ariaLabel="Desktop visitors, with each point labelled"
/>Give an annotation the same z as the series it belongs to — the series' display name, or its field name when you pass no labels. That keeps it in one focus group with the line, so the tooltip stays a single row instead of growing one per layer. Build marks outside render: like data, they are compared by identity.
Accessibility
ariaLabelis required and names the figure; addariaDescriptionwhen the takeaway needs a sentence.- The chart surface is in the tab order. Arrow keys move between points,
HomeandEndjump to the first and last,Enterand Space pin the tooltip, andEscapedismisses it. - Color alone never carries the distinction — the legend, tooltip, and axis labels all name the series.
- When exact values matter, put a real table or a short summary next to the chart — HTML the reader can select, translate, and print.
tooltip={false}keeps the focus stops but removes their live region, so screen readers land on silent points. Turn it off only for a decorative chart.
Examples
Default
Multiple Series
Linear
Step
Dots
Custom Dots
Dot Colors
Axes
Labels
Interactive
Custom Labels
API Reference
LineChart
Line chart. Give it rows plus the fields to read: one `y` field per series for wide rows, or a single `y` with `series` for long rows.
| Prop | Type | Default | |
|---|---|---|---|
readonly unknown[] | — | ||
string | — | ||
readonly string[] | string | — | ||
string | — | ||
readonly string[] | — | ||
Readonly<Record<string, string>> | — | ||
string | — | ||
union | "natural" | ||
number | 2 | ||
boolean | false | ||
"x" | "y" | boolean | "x" | ||
boolean | true | ||
boolean | false | ||
ChartFormat | — | ||
ChartFormat | — | ||
readonly ChartMarkLayer[] | — | ||
readonly ChartMarkLayer[] | — | ||
string | — | ||
string | — | ||
union | "group-x" | ||
number | — | ||
'point' | 'pointer' | 'group-center' | "group-center" | ||
boolean | true | ||
ChartAnimate | the design system's chart motion ({ type: "spring", stiffness: 170, damping: 26 }) | ||
number | — | ||
number | — | ||
number | — | ||
number | — | ||
string | — | ||
ReactNode | function | — | ||
ReactNode | — | ||
Last updated on 9/26/2026