Skip to content
microcharts
ReferenceDecisionPercentileLadder

PercentileLadder

PercentileLadder puts the chosen percentiles on one zero-anchored track, drawing the tail strongest.

encodes
tick position on a zero-anchored strip
precision
high — point estimates per percentile
nodes
≤ 8

A service answers at 124.5 ms for half its requests and 1,661.13 ms for the slowest 1%. PercentileLadder puts both ticks on one zero-anchored track, where the distance between them is the reading. Graduated height and accent make the tail read strongest, because the tail is usually the SLO question. The origin is never cropped: cropping it would distort every gap. A log transform for long tails is never silent — scale="log" applies only when every sample is strictly positive, and an in-chart log tag renders whenever it does.

PercentileLadder
interactive · 5.82 kB · static · 3.41 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

Use it for latency SLOs in a sentence, the tail per endpoint in tables, and payment-size spread. For the odds of an outcome use QuantileDots; for the full distribution shape use 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

When every sample is identical the ladder collapses to a single tick instead of stacking overlapping ones. A single value at or below zero makes scale="log" fall back to linear, and no log tag renders, because the transform was not applied. Below 56 px wide the tick labels drop out rather than overlap.

Four homes

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