# Sparkline (/docs/charts/sparkline)

A quarter of revenue is six numbers in a row, and what you want from them is the shape. Sparkline draws those ordered
values as one line, sized for a sentence, a table cell, or a KPI card. The default is the line plus an accent dot on the
last point, which marks where "now" is; fill, a normal-range band, and min/max dots are props you turn on.

```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.

## Try it

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

<Sparkline
  data={[6, 14, 20, 12, 12, 14, 11, 19, 27, 23, 25, 25]}
  curve="smooth"
  dots="minmax"
  label="last"
/>
```

## When to use it

Use it for an inline trend, a table-cell trend, a KPI sparkline, and dense dashboards. It won't answer part-to-whole
comparisons or 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.

**default**

```tsx
// data alone → an intrinsic 80×20 box
<Sparkline data={[3, 5, 4, 8, 6, 9]} />
```

**fixed size**

```tsx
// width & height are viewBox units — they also set the pixel box
<Sparkline data={[3, 5, 4, 8, 6, 9]} width={200} height={48} />
```

**responsive**

```tsx
// omit width/height, let CSS drive the width — the viewBox keeps the ratio
<div style={{ width: "100%", maxWidth: 320 }}>
  <Sparkline data={[3, 5, 4, 8, 6, 9]} style={{ width: "100%", height: "auto" }} />
</div>
```

## Variants

Shape, fill, a normal-range band, and dots are props on the same data. Each is off by default: at word size an extra
fill or band takes pixels away from the line itself. Open the Code tab on any demo to see the call.

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

`curve="smooth"` interpolates with monotone cubic tangents, so the curve stays inside the range its own points span. A
spike back down to zero cannot bow below zero on the way, and a filled area cannot cross its baseline.

```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 doesn't
render 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, because a zero-span domain maps to the middle of the
range, so flat data reads as level rather than 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. An envelope is used instead of a stride sample so that a single spike at point 1,500 of 2,000 still
renders. 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).

**In a sentence**

```tsx
<p>
  p95 latency this week{" "}
  <span className="mc-inline">
    <Sparkline data={[48, 45, 44, 40, 38, 36, 33, 31]} width={64} height={16} dots="none" summary={false} />
  </span>{" "}
  — trending down.
</p>
```

**In a table cell**

```tsx
<tr>
  <td>checkout-api</td>
  <td>
    <Sparkline data={[48, 45, 44, 40, 38, 36, 33, 31]} width={64} height={18} dots="none" summary={false} />
  </td>
  <td>31 ms</td>
</tr>
```

**In a KPI card**

```tsx
<div className="kpi">
  <span className="figure">1,600</span>
  <span className="unit">concurrent, now</span>
  <Sparkline data={[1240, 1310, 1290, 1420, 1380, 1510, 1470, 1600]} width={90} height={28} fill summary={false} />
</div>
```

**In a tab header**

```tsx
<button className="tab">
  CPU <Sparkline data={[62, 65, 61, 68, 70, 66, 72, 75]} width={40} height={14} dots="none" summary={false} />
</button>
```

## 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}` with no `title` 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, press `Escape`, or
press outside the chart. 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).
