# BoxPlot

The spread of a measurement, per category.

- Group: Charts
- Import: `import { BoxPlot } from '@misoto22/folio/charts'`
- Page: https://ui.misoto22.com/components/box-plot/
- Related: histogram, scatter-chart

## 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.

## Anatomy

- **Figure frame** (required) — ChartFigure’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.
- **Boxes** (required) — <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.
- **Notch** — notch 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 dots** — showOutliers, 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 table** (required) — The 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.

## 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.

## BoxPlot

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.

### Props

- `config` — `ChartConfig` default `DEFAULT_CONFIG`. The single series — its label and its paint. Only the FIRST entry is read; a box plot has one measurement and as many categories as it has boxes.
- `data` (required) — `BoxPlotDatum[]`. One entry per category, as raw observations or as a summary.
- `title` (required) — `string`. What the chart shows, in a sentence a reader could act on. Required, and announced to a screen reader even when it is not printed.
- `showTitle` — `boolean`. Prints the title above the plot instead of hiding it from sight.
- `description` — `ReactNode`. A line under the title — the unit, the window, the caveat.
- `children` (required) — `ReactNode`. The composed parts — axes, grid, tooltip, and `<BoxPlot.Boxes>`.
- `className` — `string`. Merged onto the figure, last, so a call site can size or space it.
- `chartProps` — `ComponentProps<typeof RechartsBarChart>`. Escape hatch onto the raw Recharts chart element.
- `orientation` — `BoxPlotOrientation` default `'vertical'`. Which way the boxes run. Reach for `horizontal` when the category names are long enough to need rotating under a column.
- `formatValue` — `(value: number) => string` default `defaultTick`. Formats every number the chart prints — ticks, tooltip, table.
- `hideDataTable` — `boolean` default `false`. Drops the hidden table view. Only correct when the page prints the data itself.
- `empty` — `ChartEmptyProps | false`. What the chart shows when it has nothing to draw. `false` keeps the axes, for a chart whose emptiness is itself the reading.

## Example — default

```tsx
import { BoxPlot } from '@misoto22/folio/charts'

<BoxPlot
  title="Response time by region"
  showTitle
  description="One box per edge location, 24 hours"
  data={data}
>
  <BoxPlot.Grid />
  <BoxPlot.XAxis />
  <BoxPlot.YAxis label="ms" />
  <BoxPlot.Tooltip />
  <BoxPlot.Boxes />
</BoxPlot>
```

## Example — orientation

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { BoxPlot, type BoxPlotOrientation } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={orientation}
    onValueChange={(next) => next && setOrientation(next as BoxPlotOrientation)}
    aria-label="Orientation"
  >
    {ORIENTATIONS.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <BoxPlot title="Response time by region" data={data} orientation={orientation}>
    <BoxPlot.Grid />
    <BoxPlot.XAxis />
    <BoxPlot.YAxis />
    <BoxPlot.Tooltip />
    <BoxPlot.Boxes />
  </BoxPlot>
</div>
```

## Example — notched

```tsx
import { BoxPlot } from '@misoto22/folio/charts'

<BoxPlot
  title="Checkout latency by variant"
  showTitle
  description="Notch width falls with the square root of the sample"
  data={data}
>
  <BoxPlot.Grid />
  <BoxPlot.XAxis />
  <BoxPlot.YAxis label="ms" />
  <BoxPlot.Tooltip />
  <BoxPlot.Boxes notch />
</BoxPlot>
```
