Skip to content
microcharts
ReferenceDecisionPercentileLadder

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.

PercentileLadder
interactive · 5.65 kB · static · 3.27 kB

Install

Import & usereact/percentile-ladder
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"/>
Needs package + stylesheetSet up with AI

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

scale + percentiles + locale

Edge cases

all values equal — collapses to one tick
log falls back silently on any value ≤ 0
labels drop below 56 px wide

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

PropTypeNotes
data*number[]Raw sample; quantiles are derived.
psnumber[]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.
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