# Line chart

> Trends over time as smooth lines, with a crosshair readout of every series, a dashed comparison period, target lines and direct end labels.

- Type: React component (Charts & Stats)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add line-chart`
- Docs: https://wingo-ui.com/components/line-chart

## Interaction

Each line draws itself from left to right the first time the chart scrolls into view, the series a beat apart. Move the mouse over the plot and a crosshair snaps to the nearest point, the dots swell and a readout lists every visible series (it flips to the other side near the right edge). On a phone, tap a point to pin the readout above your finger, drag sideways to scrub through the points (a vertical drag still scrolls the page), tap the same point again or anywhere outside to clear it. With the keyboard, Tab to the plot: Left / Right walk the points, Home / End jump to the ends, PageUp / PageDown move by ten, Enter fires onDatumClick and Escape clears; a live region reads each point. Click or tap a legend entry to hide its series (the others rescale and glide), hover or focus one to highlight it. When the data changes, the lines morph to their new shape.

## Installation

```bash
npx wingo-ui@latest add line-chart
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/line-chart` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/line-chart.tsx`

npm dependencies: `npm i motion d3-shape && npm i -D @types/d3-shape`

Wingo UI dependencies (installed automatically):

- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))

## Usage

```tsx
import { LineChart } from "@/components/ui/line-chart"

const data = [
  { month: "2026-07", revenue: 106300, expenses: 72400 },
  { month: "2026-08", revenue: 97500, expenses: 69900 },
  { month: "2026-09", revenue: 128450, expenses: 81200 },
]

export function RevenueChart() {
  return (
    <LineChart
      data={data}
      xKey="month"
      series={[
        { key: "revenue", label: "Revenue" },
        { key: "expenses", label: "Expenses" },
      ]}
      valueFormat="currency"
      locale="en-US"
      referenceLines={[{ value: 120000, label: "Target $120,000" }]}
      label="Revenue vs. expenses"
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` | `ChartDatum[]` |  | One row per x position, in drawing order (the chart does not sort): { month: "2026-09", revenue: 128450, expenses: 81200 }. null values leave a gap. |
| `xKey?` | `string` | `"date"` | The row key for the x axis. ISO days ("2026-09-24") and months ("2026-09") print as local dates. |
| `series?` | `LineChartSeries[]` |  | Which keys to draw, in color order: { key, label?, color?, name?, dashed?, strokeWidth? }. dashed draws a comparison period at 70% with a muted tooltip row. Inferred from the numeric keys of the first rows when omitted. |
| `colors?` | `string[]` |  | Per-position palette overrides: a slot ("series-3"), a tone ("neutral", "brand") or any CSS color. |
| `height?` | `number` | `240` | Plot height in px, reserved before the chart measures so nothing jumps. |
| `valueFormat?` | `ChartValueFormat = "number" \| "compact" \| "currency" \| "percent"` | `"number"` | How values print in the tooltip, ticks, end labels and table. Percent takes whole numbers (12.4 -> "12.4%" in en-US). |
| `currency?` | `string` | `"USD"` | ISO 4217 code for valueFormat currency, printed with the narrow symbol ("$128,450"). |
| `decimals?` | `number` |  | Fraction digits in the tooltip, labels and table; auto by magnitude when unset. Ticks stay short. |
| `formatValue?` | `(value: number, context: { series: string; kind: "tooltip" \| "tick" \| "label" \| "table" }) => string` |  | Replaces the built-in value formatting everywhere. |
| `formatX?` | `(value: unknown, context: "axis" \| "tooltip" \| "table") => string` |  | Replaces the x label formatting (short on the axis, long in the tooltip and table). |
| `locale?` | `string` | `""` | Locale for numbers and dates. Empty uses the UIProvider / LocaleProvider locale. |
| `yDomain?` | `LineChartDomain = [number \| "auto", number \| "auto"]` | `"[\"auto\", \"auto\"]"` | Fixed value bounds (0..100 for percentages). Auto floats around the data and the reference lines, then rounds to nice ticks. |
| `curve?` | `"monotone" \| "linear" \| "natural" \| "step"` | `"monotone"` | Line interpolation. monotone never overshoots a data point; step for values that hold until they change. |
| `strokeWidth?` | `number` | `2` | Line width in px; a series' own strokeWidth wins. |
| `showDots?` | `boolean` | `false` | A dot on every point, up to 31 points (12 on plots narrower than 480px); above that only the inspected point gets one. |
| `showArea?` | `boolean` | `false` | A soft 16% gradient under the line when exactly one series is visible (use area-chart for several). |
| `connectNulls?` | `boolean` | `false` | Bridge missing values instead of breaking the line. |
| `endLabels?` | `boolean` | `false` | The last value and the series name at the right end of each line (up to 4 series). Under 360px the label keeps only the short value ($128K) and a legend names the lines (even with showLegend off); under 240px it hides. A colliding label is dropped, never nudged; the legend and tooltip still carry it. |
| `showLastValue?` | `boolean` | `false` | A dot with a breathing halo on the last point of each series, for a live-data feel. With reduced motion or animated off the halo stays still. |
| `showGrid?` | `boolean` | `true` | Hairlines at the value ticks. |
| `showXAxis?` | `boolean` | `true` | Date / category labels under the plot, thinned from the newest backwards so they never collide. |
| `showYAxis?` | `boolean` | `true` | Value tick labels, compact for big numbers ("$128K"). |
| `yAxisSide?` | `"start" \| "end"` | `"start"` | end puts the ticks on the right, finance style. |
| `yAxisWidth?` | `number` | `0` | Minimum width of the value axis in px; 0 sizes it to the tick labels. Give stacked or synced charts the same value so their plots and crosshairs line up. |
| `showLegend?` | `boolean` | `true` | The legend; it only renders with two or more series (one series is named by the card title). |
| `legendPosition?` | `"top" \| "bottom"` | `"top"` | Legend above or below the plot. |
| `legendToggle?` | `boolean` | `true` | Legend entries are buttons that hide and show their series. The last visible series cannot be hidden. |
| `showTooltip?` | `boolean` | `true` | The floating readout. When off, the crosshair, keyboard and screen reader announcements still work. |
| `tooltipMode?` | `"auto" \| "float" \| "fixed"` | `"auto"` | float rides beside the crosshair, fixed pins above the plot (never under a finger); auto is fixed on touch screens. |
| `referenceLines?` | `{ value: number; label?: string; color?: string }[]` |  | Dashed target or average lines with a label ("Target $120,000"); always inside the value range. |
| `animated?` | `boolean` | `true` | Draw-in on first view and morphs on data changes. Reduced motion always shows the final state. |
| `loading?` | `boolean` | `false` | A line-shaped skeleton in the plot's box. |
| `emptyState?` | `ReactNode` |  | Replaces the built-in empty state (shown when data is empty or every value is null). |
| `label?` | `string` | `""` | Accessible name of the figure and caption of its data table. Empty joins the series names. |
| `description?` | `string` | `""` | A one-sentence takeaway for screen readers ("Revenue grew 12.4% in September"). |
| `surface?` | `string` | `""` | The color behind the chart, used for dot rings. Empty uses the surface token; set it when the chart sits on a card. |
| `hiddenSeries?` | `string[]` |  | Hidden series keys when you control legend toggling; pair with onHiddenSeriesChange. defaultHiddenSeries for uncontrolled. |
| `defaultHiddenSeries?` | `string[]` |  | Series keys hidden at first, when the legend toggles on its own. |
| `onHiddenSeriesChange?` | `(keys: string[]) => void` |  | Called when a legend entry hides or shows a series. |
| `activeIndex?` | `number \| null` |  | The inspected x index when you control it, so two charts can share one crosshair. |
| `defaultActiveIndex?` | `number \| null` |  | The index inspected at first (a pinned readout), uncontrolled. |
| `onActiveIndexChange?` | `(index: number \| null) => void` |  | Called as the crosshair moves (mouse, touch or keyboard) and with null when it clears. |
| `onDatumClick?` | `(event: { index: number; datum: ChartDatum; series?: string }) => void` |  | A click or tap on the plot (the nearest point; series is the nearest line) or Enter on the inspected point. |
| `labels?` | `Partial<LineChartLabels>` |  | Strings: empty, loading, table, inspectHint, showSeries, hideSeries, compare ((comparison)), period (Period). |
| `classNames?` | `Partial<Record<LineChartSlot, string>>` |  | Classes for legend, plot, tooltip, table, grid, xAxis, yAxis, line, area, dot, endLabel, referenceLine, crosshair and inspect. |

## Parts

### DEFAULT_LINE_CHART_LABELS (constant)

The English strings the chart uses (empty and loading text, the data table and its Period column, the comparison suffix, the keyboard hint, show and hide series). Spread it and override what you translate.

### LineChartLabels (type)

The shape of labels: the chart kit's shared strings plus compare and period.

### LineChartSeries (type)

One line: the shared { key, label?, color?, name? } plus dashed? (a comparison series, drawn at 70% opacity) and strokeWidth?.

## Examples

- **Single series, neutral**: colors neutral, showArea and showDots for a calm KPI line.
- **Compare period**: A dashed series for last year and a trend badge in the header.
- **Daily, 90 points**: curve linear, labels thin from the newest backwards, yAxisSide end.
- **End labels**: Four series named at their ends; colliding labels drop, never nudge.
- **Synced crosshair**: Two charts driven by one activeIndex.
- **Loading and empty**: The skeleton and the empty state in the plot's box.
- **Phone, 360px**: The pinned readout, compact ticks and thinned labels.

## See also

- [Area chart](https://wingo-ui.com/components/area-chart) ([Markdown](https://wingo-ui.com/components/area-chart.md))
- [Bar chart](https://wingo-ui.com/components/bar-chart) ([Markdown](https://wingo-ui.com/components/bar-chart.md))
- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Animated Counter](https://wingo-ui.com/components/animated-counter) ([Markdown](https://wingo-ui.com/components/animated-counter.md))

---

Canonical: https://wingo-ui.com/components/line-chart
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
