# Sparkline (/docs/charts/sparkline)

Sparkline draws a compact trend over ordered values. Reach for it when direction and shape matter more than exact points
— inline in a sentence, inside a table cell, or as the quiet backbone of a KPI card.

```tsx
<Sparkline data={[3, 5, 4, 8, 6, 9]} width={120} height={32} title="Weekly revenue" />
```

## Install

```tsx
import { Sparkline } from "@microcharts/react/sparkline";

<Sparkline data={[3, 5, 4, 8, 6, 9]} title="Weekly revenue" />
```

Setup (package + stylesheet): [Quickstart](/docs/quickstart#set-up-with-an-ai-agent) or paste [`/agent-setup.md`](/agent-setup.md) into your agent.


## When to use it

- **Good for** — an inline trend, a table-cell trend, a KPI sparkline, dense dashboards.
- **Avoid for** — part-to-whole comparisons or reading exact category values. Use [SparkBar](/docs/charts/sparkbar) for
  discrete magnitudes, or [Bullet](/docs/charts/bullet) for a value against a target.

## Sizing

`width` and `height` are viewBox units that also set the rendered pixel box. Omit them and drive the width from CSS for
a chart that fills its container — the viewBox keeps the aspect ratio.

## Variants

Shape, fill, a normal-range band, and dots are all props — same data, different read. Flip any of them to the Code tab.

```tsx
<Sparkline data={[3, 5, 4, 8, 6, 9]} curve="smooth" />
```

```tsx
<Sparkline data={[3, 5, 4, 8, 6, 9]} fill />
```

```tsx
<Sparkline data={[3, 5, 4, 8, 6, 9]} band={[4, 8]} />
```

```tsx
<Sparkline data={[3, 5, 4, 8, 6, 9]} dots="minmax" label="last" />
```

```tsx
// the extremes, labelled directly — no axis needed
<Sparkline data={[3, 5, 4, 8, 6, 9, 2, 7]} dots="minmax" label="minmax" />
```

## Edge cases

```tsx
// null = "no measurement here" — the line breaks, never interpolates
<Sparkline data={[3, 5, null, null, 6, 9, 4, 7]} />
```

```tsx
<Sparkline data={[7]} />
```

```tsx
<Sparkline data={[5, 5, 5, 5, 5, 5]} />
```

A `null` means "no measurement here" — the line breaks at the gap and never interpolates across it, so an outage can't
masquerade as a smooth trend. A single point has no line to draw; it sits centered in the plot, visible as the default
endpoint dot. An all-equal series renders on the vertical mid-line — a zero-span domain maps to the middle of the range,
so flat data reads as level, not as bottomed-out.

Past `maxPoints` (default 200) the drawn line decimates to a min/max-preserving envelope: every spike keeps its true
position and height, gaps stay gaps, and the summary, dots, and hover values still come from the raw data — only the
path gets lighter. Pass `maxPoints={Infinity}` to opt out.

```tsx
// renders ≤ 200 line points; the spike at i=1500 survives
<Sparkline
  data={Array.from({ length: 2000 }, (_, i) =>
    i === 1500 ? 98 : 50 + Math.sin(i / 40) * 24 + ((i * 13) % 7),
  )}
/>
```

## Four homes

The same chart, sized for four real contexts. Each preview is the public component plus `styles.css` — no docs-only
wrappers. Sentence placements use `mc-inline`; see [Composition](/docs/composition) and [Sizing](/docs/sizing).

## Why this default

A bare line plus one accent dot on the last point — no fill, no band, no min/max dots. The shape of the series is the
whole story until you ask for more, and the endpoint dot marks where "now" is. Min/max dots, fill, and the band are
opt-in because at word size they compete with the line for the same few pixels; adding them by default would make every
sparkline noisier for the common case that only needs direction and range. Long series decimate to a min/max-preserving
envelope rather than a naive stride sample, so a single spike at point 1,500 of 2,000 still shows up — silently dropping
it would be the more common bug in a naive implementation, and a chart that hides its own spikes is dishonest by
construction.

## Accessibility

By default the chart is an `img` whose accessible name is generated from the data. The example above is announced as:

> Weekly revenue. Trending up 200%. Range 3 to 9. Last value 9.

Pass a `title` to name it, or `summary={false}` to make it decorative when the surrounding text already says what it
shows. The interactive entry adds a polite live region that reads each focused point as you arrow through it.

The interactive entry follows the shared [interaction contract](/docs/accessibility#one-interaction-contract):
arrow keys rove between units on both axes, `Home` and `End` jump to the ends, and a click, tap, `Enter` or
`Space` selects a unit — pinning its readout so it survives blur, until you select it again or press `Escape`.
On touch, a tap pins and a drag scrubs.

## Props

| Prop | Type | Description |
| --- | --- | --- |
| `data` (required) | `number[]` | The series. null/NaN are gaps. |
| `curve` | `"linear" \| "smooth" \| "step"` | Line shape. |
| `fill` | `boolean` | Zero-anchored area under the line. |
| `band` | `[number, number]` | Constant normal-range band. |
| `dots` | `"auto" \| "minmax" \| "none"` | Endpoint or min/max dots. |
| `label` | `"none" \| "last" \| "minmax"` | Direct value labels: endpoint, or the extremes. |
| `maxPoints` | `number` | Line-point cap (default 200); longer series decimate min/max-preserving. |
| `title` | `string` | Accessible name; joins the auto summary. |
| `summary` | `string \| false` | Override or disable the auto summary. |
| `animate` | `boolean` | (interactive) Opt-in entrance motion when the chart mounts client-side — add `import "@microcharts/react/motion"` once. Inert on the server, on hydrated server HTML, and under `prefers-reduced-motion`. |

Plus the shared grammar — `data`, `domain`, `color`, `title`, `summary`, `format` — and the layout props (`width`, `height`, `className`, `style`) that every chart accepts. Interactive entries also share `animate` and `live`, and — wherever a chart has more than one navigable unit — `onActive`, `onSelect`, `selectedIndex` and `defaultSelectedIndex`; and — wherever the chart shows a hover value — `readout`. See [the shared grammar](/docs/quickstart#the-shared-grammar).
