# Quickstart (/docs/quickstart)

## Set up

Prefer an agent: paste the prompt once. It installs the package, imports the stylesheet at your app root, records the
conventions in your agent-rules file, and verifies with a first chart. Or do the two steps yourself under
[Set up manually](#set-up-manually).

### Set up with an AI agent

Hand the prompt below to your coding agent: **Cursor, Claude Code, GitHub Copilot, Codex**, or any model, large or
small. Works on a fresh repo or an existing one. It's also served verbatim at [`/agent-setup.md`](/agent-setup.md).

<div className="agent-prompt">

```md title="Paste into your agent"
Set up **@microcharts/react** in this repo — word-sized, zero-dependency React charts (React 18 or 19 peer, ESM-only).
Complete every step in order; don't stop to ask.

## 1 · Install

- If this repo is not already a React 18/19 app, scaffold one first (Vite `react-ts` or Next.js), then continue.
- Install `@microcharts/react` with this repo's package manager.
- Import the stylesheet once at the app root (both package + stylesheet are required):
  - Next.js App Router: `app/layout.tsx`
  - Next.js Pages Router: `pages/_app.tsx`
  - Vite / other React apps: the entry file (e.g. `src/main.tsx`) `import "@microcharts/react/styles.css"`
- Charts inherit the page font. If labels look like a serif fallback, set `:root { --mc-font: system-ui, sans-serif; }`.

## 2 · Docs — fetch small, on demand

- https://microcharts.dev/llms.txt — index: every chart + guide links. Read this now.
- https://microcharts.dev/catalog.json — full API. Do **not** read the whole file. Find the chart by `slug`, then use
  `sharedProps` + that chart's `props`. If the chart has `sharedInteractive` / the document has `howToRead`, follow
  those; otherwise use the chart's `.md` for interactive callbacks. Read that chart's `gotchas` before writing props.
  Never invent props.
- https://microcharts.dev/docs/charts/<slug>.md — one chart's page (import path, edge cases). Fetch only for charts you
  use — pick the slug from the index.
- https://microcharts.dev/docs/quickstart.md — grammar + interactive contract, if you need more than the rules below.
- Optional: if this client speaks MCP, `npx -y @microcharts/mcp` serves the same catalog as tools, plus a renderer —
  https://microcharts.dev/docs/mcp.md

If you can't fetch URLs, skip this step — the rules below are enough to render a first chart correctly.

## 3 · Record the conventions

Append the rules below as a `## microcharts` section in `AGENTS.md` (create the file if missing — Cursor, Copilot, and
Codex read it). If this repo uses Claude Code, mirror it: `ln -s AGENTS.md CLAUDE.md` on macOS/Linux, or copy the
section into `CLAUDE.md` on Windows.

- One chart = one subpath import, e.g. `import { Sparkline } from "@microcharts/react/sparkline"`. The default export is
  static — RSC-safe, zero client JS. Add `/interactive` only when hover, keyboard, touch, or selection is needed. That
  entry is a client component: in Next.js App Router, import it from a `"use client"` file.
- `data` alone always renders; single-value charts take `value` instead. Shared prop names mean the same thing
  everywhere — see `sharedProps` in `/catalog.json`. Chart-specific props and callbacks live on the chart's catalog
  entry and its `.md` page; don't memorize them.
- Interactive entries share one contract: hover/arrows activate; click/tap/Enter/Space select; Escape or a press outside
  the chart clears. Prefer each chart's `sharedInteractive` list when present (lean scalars may take only `onSelect`;
  MinimapStrip uses `onWindowChange` in chart `props`). For entrance motion: pass `animate` and import
  `@microcharts/react/motion` once on the client.
- These charts are word-sized. `width`/`height` are viewBox units with an authored maximum per chart (`maxWidth` /
  `maxHeight` in `/catalog.json`); stay inside it and scale with CSS beyond it, because past that box the geometry stops
  scaling and the extra room is whitespace.
- `null`, `NaN`, or non-finite values in `data` are gaps, never zeros.
- Accessibility is automatic (`role="img"` + a generated summary). Inline in a sentence: wrap in
  `<span className="mc-inline">` and pass `summary={false}`.
- Theme with `--mc-*` or `data-mc-theme` (omit for default), or `defineTheme` from `@microcharts/react/theme`. Charts
  paint no background. Prefer tokens; `color` / categorical `colors[]` are valid when the chart's docs say so.
- Annotations are children: `Threshold`, `TargetZone`, `Marker`, `Callout` from `@microcharts/react/annotations`.
- Never add another charting dependency. No pie or gauge — use `Bullet`, `SegmentedBar`, or `MicroDonut`.

## 4 · Verify

Render `<Sparkline data={[3, 5, 4, 8, 6, 9]} title="Weekly revenue" />` in a real page, then run the dev server or
build. Self-check:

- Chart missing or unstyled → the styles.css import isn't at the root (step 1).
- Labels render in a serif font → set `--mc-font` (step 1).

Finish with a short report: the installed version, the file importing styles.css, the file holding the conventions, and
the page rendering the test chart.
```

<AgentPromptCopy />

</div>

The prompt fetches small, targeted files — the [`/llms.txt`](/llms.txt) index first, then one chart's `.md` page at a
time — so it stays inside the context window of smaller models and reads today's real API. The rules are inlined too, so
it still works when the agent can't reach the network.

> **Does your assistant speak MCP?** Add the server instead of pasting the prompt, and it can search the catalog and render charts as tool calls rather than reading docs: `npx -y @microcharts/mcp`. Per-client config and the tool reference are in [MCP server](/docs/mcp). Use both: the prompt wires the library into your repo, the server answers chart questions while you work.

### Set up manually

Two steps, once, at your app root: install the package, import the stylesheet. Both are required.

**1 · Install.** React 18 or 19 is a peer dependency; there are no other runtime dependencies.

**2 · Import the stylesheet once** at the root of your app. It carries every theming token and chart style in a
low-specificity cascade layer, so your own styles always win. What counts as the root depends on your framework:
anywhere that runs once, before any chart renders.

#### Next.js

```tsx title="app/layout.tsx"
import "@microcharts/react/styles.css";
```

#### Vite / CRA / other React apps

```tsx title="src/main.tsx"
import "@microcharts/react/styles.css";
```

> Charts **inherit your page's font**. If the surrounding page never sets a `font-family` (some minimal starters and sandboxes don't), SVG text falls back to a serif and labels look wrong — give your page a font, or set one just for charts: ```css :root { --mc-font: system-ui, sans-serif; } ``` Full detail — why it inherits, and per-scope fonting — in [Theming](/docs/theming#the-font).

If your app declares its own cascade layers (Tailwind v4 does), the import order of this stylesheet decides whether your
utility classes or the library's rules win on a chart. The mechanism and both fixes are in
[Theming → Where the library's rules sit in the cascade](/docs/theming#where-the-librarys-rules-sit-in-the-cascade).

## Your first chart

Every series chart renders from `data` alone; single-value charts take `value`. Either way it works in a React Server
Component with **zero client JavaScript**: it's pure SVG, and its accessible name is generated from the data.

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

export default function Page() {
return <Sparkline data={[3, 5, 4, 8, 6, 9]} title="Weekly revenue" />;
}
```

### Try it live

No install needed. Open a full Vite + React app with the package installed in StackBlitz — static and interactive
charts, inline and block, four themes, running in your browser.

<StackBlitzSandbox />

## The shared grammar

One idea runs through the whole catalog: **the same prop name means the same thing on every chart.** Learn these once
and they transfer everywhere — a new chart type is a new data shape, never a new vocabulary. The data prop alone always
renders something correct; everything else is opt-in.

| Prop       | Type                                        | What it does                                                                                                                                                              |
| ---------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`     | `(number \| null)[] \| per-chart shape`     | The series. `null` / `NaN` are gaps, never zeros. The one required prop; each chart documents its own shape when it isn't a plain number array.                           |
| `domain`   | `[min, max]`                                | Fix the value range instead of auto-fitting — see [Formatting & scale](/docs/formatting#scale-the-domain-decision).                                                       |
| `color`    | `string`                                    | Mark color: any CSS color or a token (`"var(--mc-accent)"`). Semantic tokens keep their meaning — see [Theming](/docs/theming).                                           |
| `title`    | `string`                                    | Names the chart — an SVG `<title>`, so it becomes the accessible name and the hover tooltip, not visible text.                                                            |
| `summary`  | `string \| false`                           | Override the auto-generated accessible sentence, or pass `false` to drop it — with no `title` that marks the chart decorative — see [Accessibility](/docs/accessibility). |
| `format`   | `Intl.NumberFormatOptions \| (n) => string` | How numbers render in labels and summaries — see [Formatting & scale](/docs/formatting).                                                                                  |
| `positive` | `"up" \| "down"`                            | Which direction is good, so color and arrows agree with it (lower latency is `"down"`).                                                                                   |
| `label`    | _per chart_                                 | A short direct label next to the mark, in a reserved gutter — never an axis or legend.                                                                                    |
| `dots`     | _per chart_                                 | Which individual points to emphasize (e.g. `"minmax"` on a line).                                                                                                         |

Not every prop applies to every chart: a bar chart has no `dots`, and the single-value charts take `value` in place of
`data`. But wherever a concept exists, its prop name and meaning are always these. Each one has a deeper home:
[Formatting & scale](/docs/formatting) owns `format` and `domain`, [Theming](/docs/theming) owns `color`, and
[Accessibility](/docs/accessibility) owns `summary`.

Alongside the grammar sit the layout props: `className` and `style` on every chart, plus `width` and `height` on the
charts whose geometry is sized that way (the fixed-glyph ones size from `style` or their own `size` / `cell` knob) and
`id`, which opts into `<title>`/`<desc>` + `aria-labelledby` naming; see [Sizing](/docs/sizing). The i18n props are
`locale` (BCP-47, for number and date formatting) and `strings` (the summary/label templates), plus `seriesStrings` on
the multi-series charts — see [Accessibility](/docs/accessibility#internationalization-and-rtl). Everything else is
specific to a chart's own shape and listed in the prop table on its page. To place charts in real UI — or render several
on one shared scale with `SparkGroup` — see [Composition](/docs/composition). Annotations are children:
[`Threshold`, `TargetZone`, `Marker`, `Callout`](/docs/annotations).

## Add interactivity

Need hover, keyboard navigation, touch, or live announcements? Import the same chart from its `/interactive` entry. The
rendered output and the accessible name are identical, because the interactive entry composes its static twin. It only
**adds** props: the interaction callbacks below, plus `animate`, `strings` on the charts whose static entry doesn't take
it, `live` on the single-value charts, and `onWindowChange` on MinimapStrip. The interactive entry **is** a client
component: in a Next.js App Router page, render it from a file with `"use client"` at the top (or anywhere inside one).

```tsx
"use client";

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

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

### One interaction contract

Every interactive chart behaves the same way, so you learn it once. Hover or arrow keys make a unit **active**; a click,
tap, **Enter**, or **Space** **selects** it and pins the readout so it survives blur. **Home**/**End** jump to the ends,
and arrows rove in both axes (←/↑ previous, →/↓ next). On touch, a tap pins and a drag scrubs. What a "unit" is depends
on the chart — a point, a bar, a day cell, a segment — and each chart's page names its own.

A pin comes off three ways: select the same unit again, press **Escape** (from the chart or from anywhere on the page),
or press anywhere outside the chart. Each of them fires `onSelect(null)`.

| Prop                   | Type                                  | What it does                                                                               |
| ---------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------ |
| `onActive`             | `(datum: MicroDatum \| null) => void` | The active unit changed (pointer or keyboard). `null` when the chart clears.               |
| `onSelect`             | `(datum: MicroDatum \| null) => void` | A unit was selected or unselected — click, tap, `Enter`, `Space`, `Escape`, outside press. |
| `selectedIndex`        | `number \| null`                      | Control the selection yourself.                                                            |
| `defaultSelectedIndex` | `number \| null`                      | Seed the selection once, then let the chart own it.                                        |
| `readout`              | `boolean`                             | Show the floating value chip on hover (default `true`). `false` hides only the chip.       |

`MicroDatum` is `{ index, value, label?, formatted? }`. Two of the fields carry the value in different forms, so you
pick the one that fits. **`value`** is the raw primary number, for computing (`null` when the unit encodes nothing).
**`formatted`** is the ready-to-display string the chart's own readout would show, built with your `format`/`locale`,
for rendering. For a plain chart that's just the value (`"$0.53"`); for a two-part unit it's the whole readout
(`"48 → 68 (up 42%)"`). `index` identifies the unit (point, bar, cell, segment…), and `label` is its human name where
one exists (`"Berlin"`, `"Checkout"`). Same shape on every chart and both callbacks.

Pair the two to lift the value out of the chart. Set `readout={false}` to keep the hover crosshair but suppress the
in-chart chip, and read `datum.formatted` in `onActive` to render that value wherever you like — a KPI number, a
sentence, a table cell — without re-deriving `format`/`locale` yourself:

```tsx
const [spend, setSpend] = useState("$0.53");
<Sparkline data={cost} format="$,.2f" readout={false} onActive={(d) => d && setSpend(d.formatted!)} />;
```

Scalar charts with a single unit — Delta, Progress, StatusDot, Bullet and the rest — take `onActive` and `onSelect` too:
the one unit is still hovered, focused and activated, so the `readout={false}` pattern above works there as well. What
they don't take is `selectedIndex`/`defaultSelectedIndex` — there's no cursor to move and no index to point it at.
Fifteen of them also take `live` (default `true`): as the value updates, the new reading is announced through a polite
live region, throttled so a streaming number never spams a screen reader. Not all of them do (Bullet's only interaction
prop is `onSelect`), so check the chart's own page. Two charts opt out of the picker on purpose: `MinimapStrip` is a
range slider with its own `onWindowChange([lo, hi])`, and `TokenConfidence` moves real focus through inline text spans.
Full detail, including the live-region behavior, is on [Accessibility](/docs/accessibility#one-interaction-contract).

## Animate on mount (optional)

Interactive entries also take an `animate` prop: the chart draws on instead of appearing instantly. It's off by default,
and inert on the server and on hydrated server HTML, so it never fights SSR or flashes over painted content; a fresh
client-side mount is what plays it. Wire the engine once, client-side, then opt in per chart:

```tsx
"use client";

import "@microcharts/react/motion"; // once, client-side
import { Sparkline } from "@microcharts/react/sparkline/interactive";

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

`prefers-reduced-motion` wins unconditionally: a reader who asked for less motion gets the finished chart with no
entrance animation.

## Import paths

Import each chart from its own subpath so you only ship what you use. Every chart follows the same two-entry pattern — a
static default and an `/interactive` twin:

| Chart        | Static                             | Interactive                                    |
| ------------ | ---------------------------------- | ---------------------------------------------- |
| Sparkline    | `@microcharts/react/sparkline`     | `@microcharts/react/sparkline/interactive`     |
| SparkBar     | `@microcharts/react/sparkbar`      | `@microcharts/react/sparkbar/interactive`      |
| Delta        | `@microcharts/react/delta`         | `@microcharts/react/delta/interactive`         |
| Bullet       | `@microcharts/react/bullet`        | `@microcharts/react/bullet/interactive`        |
| ActivityGrid | `@microcharts/react/activity-grid` | `@microcharts/react/activity-grid/interactive` |

Every other chart uses the same shape — see [All charts](/docs/charts) for the full catalog, each page listing its own
import path and props.

## Compatibility

- **React 18 or 19** as a peer dependency — no other runtime dependencies, ever (`dependencies: {}`, CI-enforced).
- **ESM-only.** The package ships `"type": "module"`; there's no CommonJS build.
- **Server-component and SSR-safe.** Static entries are hook-free and listener-free, so they render to plain SVG on the
  server with zero client JavaScript — no `"use client"` needed unless you import from `/interactive`.
- **Browser floor: evergreen, ES2022.** That includes Safari from version 16.4 — a couple of ES2022 array methods used
  internally crash on older Safari, so 16.4 is the practical minimum regardless of your `tsconfig`.
