Skip to content
Folio

Charts

BoxPlot

The spread of a measurement, per category.

Usage

When to reach for it

How variable is this, across six things at once. When the shape of ONE distribution is the question it wants a Histogram; when there are few enough observations to draw them all, a ScatterChart.

Ships from@misoto22/design/charts

TSX
import { BoxPlot } from '@misoto22/design/charts'

Notes

The spread of a measurement, per category — median, middle half, reach, and the points that sit outside it.

The form to reach for when the question is "how variable is this", and the one that answers it in a tenth of the ink a histogram per category would take. Six response-time distributions fit across one screen as six boxes; as six histograms they do not fit at all.

What a box plot hides is multimodality. A box is five numbers, and five numbers cannot tell a single hump from two. A bimodal distribution — a fast cache path and a slow database path, two cohorts inside one average — draws exactly the same box as a smooth one centred in the same place, and the reader has no way to tell from the picture that the middle of the box is a value almost nothing takes. It also hides sample size: a box over six points and a box over six thousand are drawn identically, which is why count is worth carrying and why a notch, which does read count, is worth turning on when medians are being compared. When the SHAPE of one distribution is the question, reach for <Histogram>; when there are few enough observations to draw them all, reach for <ScatterChart> and plot the points.

Recharts earns its place here: the boxes need a shared value axis with real ticks, a category axis, a grid and a tooltip, which is most of a cartesian chart. What it does not have is a box mark, so each box is drawn as a custom shape over a range bar — the bar supplies the category band and the scale, and the glyph inside it is ours.

Anatomy

BoxPlot anatomy
ElementDescription
Figure framerequiredChartFigure’s <figure>, with an empty state when no category survives resolving. empty={false} keeps the axes for a chart whose emptiness is itself the reading.
Boxesrequired<BoxPlot.Boxes>: one range bar per category spanning min to max, entirely unpainted, with the glyph drawn over it. Recharts supplies the category band and the scale; the box, the median rule, the whiskers and the outlier dots are the package’s own.
Value axis<BoxPlot.YAxis>, and it is deliberately NOT anchored at zero — a box plot compares distributions, and dragging the domain to zero to be honest about bar length flattens every box into the same band of pixels. The honesty it owes is a labelled axis, which it has.
Notchnotch pinches the box in at the median by 1.58 times IQR over the square root of n — a confidence interval drawn inside the shape it belongs to. It needs count on every box; a box without one is drawn square and says nothing about it.
Outlier dotsshowOutliers, on by default. Points past Tukey’s 1.5 IQR fences are drawn one dot each, and the whiskers then stop at the most extreme observation still INSIDE the fence rather than at the fence itself, so no whisker claims a reading the data does not contain.
Hidden data tablerequiredThe sr-only table carries all five numbers per category plus the outlier count, so the figure is fully readable without seeing the glyph.

Best practices

Do

  • Carry count on every box. A box over six observations and a box over six thousand are drawn identically, and count is also what a notch reads — without it, notch is accepted and quietly does nothing.
  • Say which quantile rule produced a pre-computed summary. This component uses R type 7 when it summarises raw values, and on [1, 2, 3, 4] that puts the lower quartile at 1.75 where the median-of-the-lower-half rule puts it at 1.5 — the same data under two rules is two different pictures.
  • Hand it the raw values rather than a summary when you have them. One pass applies Tukey’s fences, splits the outliers out and fills count in, so the five numbers and the dots cannot drift apart.

Don’t

  • A box cannot tell one hump from two. A latency series with a cache path and a database path in it draws exactly the box a smooth distribution centred in the same place draws, and the middle of that box is a value almost nothing takes. When the SHAPE is the question it is a Histogram.
  • A category whose values array is empty is dropped outright — no box, no tick, no table row — because summarising it yields nothing to draw. The chart renders six boxes where seven were asked for and says nothing about the seventh.
  • One observation draws a box with no box: q1, the median and q3 are the same number, the IQR is zero, and the glyph collapses to a single rule. It is not an error and it is not a distribution, so guard it at the call site.

Examples

default

Four distributions in the ink four bars would take, handed over as raw observations — the chart sorts them, takes quartiles the R type 7 way, and stops each whisker at the most extreme reading still inside Tukey's 1.5 IQR fence, so Sydney's 402 and Frankfurt's 588 fall out as dots rather than stretching a whisker to a value nobody measured. What five numbers cannot tell you is shape: a region with two humps in it draws the same box as a smooth one centred in the same place. They cannot tell you sample size either, since a box over ten readings and a box over ten thousand are drawn identically — which is what count and BoxPlot.Boxes notch exist to fix. The value axis is not anchored at zero, because dragging it there to be honest about bar length would flatten every box into one band of pixels.

Response time by regionOne box per edge location, 24 hours
Response time by region
nameMinimumLower quartileMedianUpper quartileMaximumOutliers
Sydney182195.75207219.252361
Singapore244259271.5287.753180
Frankfurt310326.5341358.753791
Oregon268280.25290.5305.753410

orientation

Long category names are the case horizontal exists for: under a column ap-southeast-2 has to be rotated to fit, and beside a row it reads straight. The axes are composed once and resolve themselves — XAxis and YAxis each read the orientation and swap between the category role and the value role, and the grid flips which set of rules it draws — so the toggle changes one prop and nothing else in the tree. That is also why neither axis carries a label here: the axis a unit belongs to changes with the orientation, and a label pinned to YAxis would end up naming the regions.

Response time by region
Response time by region
nameMinimumLower quartileMedianUpper quartileMaximumOutliers
ap-southeast-2182195.75207219.252361
ap-southeast-1244259271.5287.753180
eu-central-1310326.5341358.753791
us-west-2268280.25290.5305.753410

notched

Summaries that arrived already computed, which is the other shape this takes — a warehouse hands back percentiles, never rows. count is what a notch needs: the pinch is 1.58 times IQR over the square root of n, so two notches that do not overlap are roughly a 95 percent test that those medians differ, and Variant B's notch is wide because its sample is 190 against the others' 4,000. That is the comparison the plain box hides, since a box over 190 readings and a box over 4,200 are drawn identically. The notch is clamped to the quartiles rather than allowed to fold the box inside out, so a sample too small for the comparison pinches to a sliver instead of reading as a rendering fault.

Checkout latency by variantNotch width falls with the square root of the sample
Checkout latency by variant
nameMinimumLower quartileMedianUpper quartileMaximumOutliers
Control1802142362623180
Variant A1762082292553100
Variant B1842262582913560

Types

TSX
export type BoxPlotOrientation = 'vertical' | 'horizontal'
export type BoxPlotDatum = BoxPlotSample | (BoxPlotSummary & { values?: never })

Accessibility

  • A box is five numbers, and five numbers cannot tell one hump from two. A bimodal distribution draws exactly the same box as a smooth one centred in the same place — the component says so in its own description rather than in a footnote.
  • It also hides sample size: a box over six points and a box over six thousand are drawn identically. Carry count, and turn on notched whenever medians are being compared.
  • Raw values are summarised with Tukey’s fences, which is stated on the page rather than assumed — a different fence rule draws different outliers from the same data.
  • The hidden data table carries all five numbers per category, so the figure is readable without seeing the glyph.