Hypnogram
React hypnogram chart — categorical step strip of which discrete state held over time. Tiny accessible SVG microchart; RSC-safe static or /interactive.
Hypnogram answers "which discrete state was the system in over time, and how choppy were the transitions?". It refuses interpolation — there are no diagonals, ever — because a state is a fact that holds until the next one, not a sample of a continuum a line chart can slope between.
Install
import { Hypnogram } from "@microcharts/react/hypnogram";// sleep — real values under “Sample data” below<Hypnogram data={sleep} states={["Awake","REM","Light","Deep"]} title="Sleep stages" />Sample data
const sleep = [ { t: 0, state: "Awake" }, { t: 8, state: "Light" }, { t: 22, state: "Deep" }, { t: 38, state: "Light" }, { t: 50, state: "REM" }, { t: 62, state: "Light" }, { t: 74, state: "Deep" }, { t: 86, state: "Light" }, { t: 98, state: "REM" }, { t: 110, state: "Awake" },];Try it
When to use it
- Good for — sleep stages, deploy / machine / incident state over time.
- Avoid for — continuous signals (Sparkline) or a single current state (StatusDot).
Sizing
Variants
Edge cases
A single entry holds its state across the whole domain — one flat run, and the summary names it directly — "1 state,
no transitions; Deep throughout." — rather than describing a one-point chart. A state present in the data but missing
from an explicit states order is never dropped: it's appended as its own row (with a dev-only console warning), so the
strip always accounts for every state it's given.
Four homes
Why this default
Right-angle steps with an explicit states order are the clinically proven read. Order is meaningful — for ordinal
states (sleep depth, incident severity) pass it explicitly; for nominal states with no rank, use mode="lanes" so the
vertical axis never implies one. Any smoothing or easing of the step corners would be a lie about the data, so the
entrance is a left-to-right clip reveal of the finished trace — the steps themselves are never interpolated.
Accessibility
The accessible name summarises the shape — "3 transitions across 4 states; longest run Awake." The interactive entry roves the runs; each announces its state and span ("Light, from 8 to 22.").
The interactive entry follows the shared 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 | Notes |
|---|---|---|
| data* | { t, state }[] | State holds from t to the next entry. |
| states | string[] | Row order top→bottom; ordinal semantics live here. |
| emphasis | string | Accents one state — the decision read. |
| mode | "steps" | "lanes" | Lanes for nominal states with no rank. |
| connectors | boolean | Vertical transition strokes (default true); off for ultra-dense strips. |
| labels | boolean | Left-gutter state names (default: on when width ≥ 96). |
| colors | string[] | Per-state lane colours (lanes mode), cycled; overrides --mc-cat-N. |
| animate | boolean | interactiveOpt-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.
Related charts
TimeInRange
React time-in-range chart — how much of a period stayed inside its corridor, with in-range percent as the headline. Tiny accessible SVG microchart; RSC-safe static or /interactive.
EtaBar
React ETA bar chart — remaining time sized by the observed rate, not linear interpolation. Tiny accessible SVG microchart; RSC-safe static or /interactive.