Line Chart

Draws one line per series across an ordered domain, themed by your design system.

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-line

It 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

  • ariaLabel is required and names the figure; add ariaDescription when the takeaway needs a sentence.
  • The chart surface is in the tab order. Arrow keys move between points, Home and End jump to the first and last, Enter and Space pin the tooltip, and Escape dismisses 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.

PropType
readonly unknown[]
string
readonly string[] | string
string
readonly string[]
Readonly<Record<string, string>>
string
union
number
boolean
boolean
boolean
boolean
ChartFormat
ChartFormat
readonly ChartMarkLayer[]
readonly ChartMarkLayer[]
string
string
union
number
'point' | 'pointer' | 'group-center'
boolean
boolean
ChartAnimate
number
number
number
number
string
ReactNode | function
ReactNode

Last updated on 9/7/2026