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" }}
legend={false}
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
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.25 | ||
boolean | false | ||
boolean | false | ||
boolean | true | ||
boolean | true | ||
ChartFormat | — | ||
ChartFormat | — | ||
readonly ChartMarkLayer[] | — | ||
readonly ChartMarkLayer[] | — | ||
string | — | ||
string | — | ||
union | "group-x" | ||
number | — | ||
'point' | 'pointer' | 'group-center' | "group-center" | ||
boolean | true | ||
boolean | true | ||
ChartAnimate | { type: "spring", stiffness: 170, damping: 26 } | ||
number | 256 | ||
number | — | ||
number | — | ||
number | — | ||
string | — | ||
ReactNode | function | — | ||
ReactNode | — | ||
Last updated on 9/7/2026