PercentileLadder
React percentile ladder chart — where a value sits on a percentile ladder. Tiny accessible SVG microchart; RSC-safe static or /interactive.
PercentileLadder answers "what does the tail look like — not just the median?". Ticks at the chosen percentiles sit on a
zero-anchored track; graduated height and accent make the tail read strongest. Tick distances carry the story, so
the origin is never cropped — and a log transform (for long latency tails) is never silent: an in-chart log tag
renders when it applies.
Install
import { PercentileLadder } from "@microcharts/react/percentile-ladder";<PercentileLadder data={Array.from({ length: 200 }, (_, i) => i < 130 ? 90 + (i % 50) : i < 180 ? 150 + ((i * 7) % 320) : i < 196 ? 480 + ((i * 11) % 900) : 1500 + ((i * 13) % 800), )} format={{ style: "unit", unit: "millisecond" }} title="Request latency"/>Try it
When to use it
- Good for — latency SLOs in a sentence, the tail per endpoint in tables, payment-size spread.
- Avoid for — the odds of an outcome (QuantileDots) or the full distribution shape (MicroBox).
Sizing
Variants
Edge cases
Four homes
Why this default
The default linear track is zero-anchored — tick distances carry the story, so cropping the origin would distort
every gap between percentiles. Height and accent grow toward the tail because the tail, not the median, is usually the
SLO question. A log transform for long tails is never silent: it only applies when every sample is strictly positive,
and an in-chart log tag renders whenever it does, so the axis is never misread as linear.
Accessibility
The accessible name states every marked percentile and the tail's multiple of the median — "p50 124.5, p90 488.1, p99 1,661.13 — the slowest 1% take 13.3× the median." The interactive entry roves ticks with ←/→ (Home/End jump to the ends), announcing each tick's value and its multiple of the median ("p99: 1,661.13 — 13.3× the median.").
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[] | Raw sample; quantiles are derived. |
| ps | number[] | Percentiles to mark (default [50, 90, 99], 2–4). |
| scale | "linear" | "log" | Log for long tails (falls back on any value ≤ 0; renders a log tag). |
| label | "ps" | "values" | "both" | "none" | What the tick labels state. |
| marks | "tick" | "dot" | Tick marks (default) or dot marks — dots read calmer over dense text. |
| 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
BenchmarkStrip
React benchmark strip chart — one value against its cohort's empirical quantiles, with percentile stated. Tiny accessible SVG microchart; RSC-safe static or /interactive.
GradedBand
React graded band chart — a measured value seated inside qualitative grade bands. Tiny accessible SVG microchart; RSC-safe static or /interactive.