BiasStrip
React bias strip chart — bland–Altman offset between two measurement methods. Tiny accessible SVG microchart; RSC-safe static or /interactive.
BiasStrip answers "do two ways of measuring the same thing systematically disagree?" — the agreement story a scatter can't tell. Each dot is one pair placed at its mean and its difference; the dashed line is perfect agreement, the accent line is the measured bias, and the faint band is the limits of agreement. Dots render at 75% opacity so overplot reads as density, and pairs beyond the limits are enlarged and re-inked so outliers read on shape, not color alone.
Install
import { BiasStrip } from "@microcharts/react/bias-strip";// pairs — real values under “Sample data” below<BiasStrip data={pairs} title="Device vs reference" />Sample data
// a ~+2 bias with noise and two pairs beyond the limits of agreementconst pairs = [ 1.8, 2.4, 1.5, 2.9, 2.1, 1.2, 2.6, 3.0, 1.9, 2.3, 6.5, 2.0, 1.7, 2.8, 2.2, -1.5, 2.5, 1.6, 2.7, 2.0,].map((d, i) => ({ a: i + d, b: i }));Try it
When to use it
- Good for — checking whether a new instrument, method, or model agrees with a reference; instrument drift in a KPI card.
- Avoid for — unpaired samples (use MicroScatter) or a single time series (Sparkline).
A correlation plot answers "do these move together?" — but two methods can correlate perfectly and still disagree by a constant offset. BiasStrip is built for that offset: it plots the difference, so a bias jumps off the zero line where a scatter would hide it on the diagonal.
Sizing
Variants
Edge cases
Empty data draws just the frame with "No data." as the summary. Under 5 pairs the limits aren't meaningful, so the band and bias line drop out and only the dots and the zero reference remain — the mean difference is still stated in the summary. When the two methods agree perfectly the band collapses to a hair around the zero line rather than vanishing, so "nothing to see" still reads as a deliberate, measured result. Pairs with a non-finite value are dropped before the stats and the count; past 40 pairs the dots are uniformly downsampled for display while the bias and limits still use every pair.
Four homes
Why this default
The y-axis is symmetric about zero so perfect agreement is always the visual center: a systematic offset then reads as
the whole cloud drifting up or down, which is exactly the decision the chart exists to surface. The band is the classic
±1.96σ limits of agreement, and limits widens or tightens it (2.58 for ~99%) without ever redefining what the marks
mean. Under five pairs the chart makes no interval claim at all — too little evidence to draw one honestly.
Accessibility
The accessible name is the bias plus the evidence-backed agreement share — "Bias +2.21 across 20 pairs; 90% within the
limits of agreement." — or the shorter "Bias +2 across 4 pairs." when there are too few pairs to draw limits.
Localizing carries the number format through: with locale="de-DE" the same chart reads "Bias +2,21 across 20 pairs;
90% within the limits of agreement." The interactive entry steps pairs ordered by mean and announces each pair's mean,
difference, and whether it clears the limits.
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* | { a; b }[] | Paired measurements. |
| limits | number | k in bias ± k·σ (default 1.96 ≈ 95% limits of agreement). |
| label | "bias" | "none" | Seat-gated bias caption (default) or hidden. |
| r | number | Base dot radius, clamped [1, 3]. |
| 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
WinProbWorm
React win-prob worm chart — win probability over a contest as a worm. Tiny accessible SVG microchart; RSC-safe static or /interactive.
Formatting & scale
Control how a chart shows numbers and dates, and what value range it's drawn against — one format prop (Intl options or a function) feeds the direct labels, the accessible summary and the interactive readout alike, locale handles separators and language, and domain fixes the scale. Formatting is cached, so it stays cheap across hundreds of charts.