Skip to content
microcharts
ReferenceFrontierHypnogram

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.

Hypnogram
interactive · 5.31 kB · static · 2.78 kB

Install

Import & usereact/hypnogram
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" },];
Needs package + stylesheetSet up with AI

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

lanes (nominal)
emphasis

Edge cases

one state, whole domain
a state outside the explicit order

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

PropTypeNotes
data*{ t, state }[]State holds from t to the next entry.
statesstring[]Row order top→bottom; ordinal semantics live here.
emphasisstringAccents one state — the decision read.
mode"steps" | "lanes"Lanes for nominal states with no rank.
connectorsbooleanVertical transition strokes (default true); off for ultra-dense strips.
labelsbooleanLeft-gutter state names (default: on when width ≥ 96).
colorsstring[]Per-state lane colours (lanes mode), cycled; overrides --mc-cat-N.
animatebooleaninteractiveOpt-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