ChangePoint
ChangePoint marks where a series stepped to a new level, shading each regime and drawing its mean.
- encodes
- break marker position + regime shading
- precision
- high
- nodes
- ≤ 6 + 3 per break
An error rate sat at 30 for two weeks, then sat at 48. ChangePoint shades the two regimes, draws a mean hairline through each, and marks the break between them, so a reader gets "errors stepped up here" rather than "errors are high". The detector is a documented heuristic, not statistics: 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. Pass your own break indices and detection switches off — the chart becomes pure annotation.
Install
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),];Try it
When to use it
Use it for context around an anomaly, an error rate / latency / cost that stepped to a new level, or annotating a known
deploy or incident with explicit breaks. Skip it for a gradual trend (Sparkline) or a plain time series with no regime
question.
Sizing
Variants
This is a mean-shift test, not a trend test. A steady ramp is still fitted as two regimes: the second variant above
reports a shift around point 10. Pass breaks explicitly when you already know the series is trending rather than
stepping.
Edge cases
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, because 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
Accessibility
The accessible name states the shift, the break, the regime means, and what followed: "Level shifted up 60% around point 14 (mean 30 → 48); stable since." With no detected shift it reads "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
| Prop | Type | Notes |
|---|---|---|
| data* | number[] | A single series. |
| breaks | "auto" | number[] | Explicit indices override the heuristic entirely — the production path. |
| maxItems | number | Max detected breaks (1–3). More regimes stop being glanceable. |
| means | boolean | Per-regime mean hairlines (default true). |
| label | "delta" | "none" | Signed % across the most recent break, in a gutter. |
| 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.