Skip to content
microcharts
ReferenceDecisionChangePoint

ChangePoint

React change-point chart — regime breaks marked on a series with mean hairlines. Tiny accessible SVG microchart; RSC-safe static or /interactive.

ChangePoint answers "when did the behavior change level?". It shades alternating regimes, draws a per-regime mean, and marks the break where one level became another — because a spike means nothing without the regime it broke. The detector is a documented heuristic, not statistics; for production anomaly pipelines, pass your own break indices and the chart becomes pure annotation.

ChangePoint
interactive · 6.22 kB · static · 3.55 kB

Install

Import & usereact/change-point
import { ChangePoint } from "@microcharts/react/change-point";// errors — real values under “Sample data” below<ChangePoint data={errors} label="delta" title="Error rate" />
Sample data
// error rate that stepped up on the 14th, then held — a clean level shiftconst errors = [  ...Array(14).fill(0).map((_, i) => 30 + ((i * 7) % 5) - 2),  ...Array(20).fill(0).map((_, i) => 48 + ((i * 5) % 5) - 2),];
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — context for an anomaly, an error rate / latency / cost that stepped to a new level, or annotating a known deploy or incident (pass explicit breaks).
  • Avoid for — a gradual trend (Sparkline) or a plain time series with no regime question.

Sizing

Variants

explicit break + gradual ramp

Edge cases

locale
short series — detection stays off
single point

With a locale, the summary and delta label format their numbers in that locale's own grouping and decimal marks. Auto-detection (breaks="auto") only runs at 8 or more points — a six-point series with an obvious jump still renders as one flat, unbroken regime unless you pass breaks explicitly; this is deliberate, since a two-segment fit on that few points is mostly noise. A single point has no line to draw and no break to find; data={[]} renders the frame with the "no data" summary and no marks.

Four homes

Why this default

Context for every anomaly — the shading names the regime a value belongs to, and the break marks where the level truly changed, so a reader sees "errors stepped up here" rather than "errors are high". The detector is a labelled heuristic: a two-segment mean-shift found by binary segmentation, accepted only when the split both cuts the variance enough and clears an effect-size threshold, so a constant series shows no break. It is a mean-shift test, not a trend test: a steady ramp is still fitted as two regimes (the ramp variant above reports a shift around point 10), so pass breaks explicitly when you already know the series is trending rather than stepping. For anomaly pipelines that already know their breakpoints, breaks={[…]} turns the detection off and the chart becomes a faithful annotation.

Accessibility

The accessible name states the shift, the break, the regime means, and what happened after — "Level shifted up 60% around point 14 (mean 30 → 48); stable since." — or, with no detected shift, "No clear level shift across N points." The interactive entry steps the points with ←/→ (value + regime) and cycles the breaks with Tab, each announcing its mean shift.

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*number[]A single series.
breaks"auto" | number[]Explicit indices override the heuristic entirely — the production path.
maxItemsnumberMax detected breaks (1–3). More regimes stop being glanceable.
meansbooleanPer-regime mean hairlines (default true).
label"delta" | "none"Signed % across the most recent break, in a gutter.
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