# Histogram

The shape of one distribution.

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

## When to reach for it

A single distribution has to be understood — two clusters, a hard floor, a pile-up at a timeout. Several distributions side by side want a BoxPlot.

## Anatomy

- **Figure frame** (required) — ChartFigure’s <figure>, with an empty state when no bucket survives binning — which is what zero finite observations produces.
- **Bars** (required) — <Histogram.Bars>, each drawn from its bucket’s own two edges rather than placed in an equal category slot, which is what lets an uneven bucket be as wide as it really is. radius is 0 by default, unlike BarChart’s: a rounded corner draws a gap between two buckets that touch.
- **Measured axis** (required) — <Histogram.XAxis>, numeric, running from the first bucket’s lower edge to the last one’s upper edge.
- **Count axis** — <Histogram.YAxis>. Under mode="frequency" it is a count; under mode="density" it is count over n times width, and the bars then enclose an area of one.
- **Binning rule** — Not a mark on screen and the most consequential part of the figure: bins takes a bucket count or the explicit edges, and the default is Freedman-Diaconis capped at 200 buckets, falling back to Sturges when the interquartile range is zero. Explicit edges are also a RANGE — an observation outside the first and last has no bucket, and is counted into the tooltip’s share and into a Below or Above row of the table rather than dropped.
- **Hidden data table** — The sr-only table prints each bucket’s two edges as its row header and its count beside them — the only exact reading a binned chart can offer, since every bar stands for a range rather than for a value. Observations outside explicit edges get their own Below and Above rows, because they have no bar anywhere.

## Best practices

### Do

- Name the binning rule on the page. The same numbers cut into eight buckets and into eighty are two different pictures, and a gap between two humps can be created or erased by moving one edge — so look at more than one width before believing a feature.
- Set mode="density" whenever the buckets are uneven. Under frequency a bucket twice as wide stands twice as tall at the same underlying rate, which is the trap pre-counted buckets from a metrics backend walk straight into.
- Give values or data, never both. data wins when both arrive, so the values array is then binned by nothing and drawn by nothing, with no warning anywhere.

### Don’t

- Do not read the tooltip’s share as a share of the bars. It is a share of the SAMPLE, so a set of buckets summing to 96% is telling you the other 4% fell outside your own edges — which is the one reading a share taken over the drawn buckets could never give, because it always sums to 100.
- Do not use it to compare several distributions. Two histograms overlaid occlude each other and six side by side do not fit; that is a BoxPlot, which spends a tenth of the ink per distribution.
- Do not assume the automatic rule kept the resolution you asked for. Freedman-Diaconis divides by the interquartile range, so a tight middle with a long tail asks for tens of thousands of sub-pixel bars — the 200-bucket cap turns that into a coarse histogram rather than a hung tab, and a coarse histogram is a different picture.
- Do not read a single-value distribution as a shape. Every observation on one number still draws: the edges become that value plus and minus a half, and the result is one honest bar that is not a distribution.

## Accessibility

- The shape is a property of the bin width, not only of the data: the same numbers cut into eight buckets and into eighty are two different pictures. The rule that drew it — Freedman–Diaconis by default — is named on the page.
- The x axis is numeric and every bar is drawn from its own two edges, so an uneven bucket is as wide as it really is rather than flattened into an equal slot.
- mode="density" corrects the trap uneven buckets create: under frequency a bucket twice as wide stands twice as tall at the same underlying rate.
- The hidden data table prints each bucket’s two edges and its count, which is the only exact reading a binned chart can offer.

## Histogram

The shape of one distribution — where the mass sits, how it leans, whether there is more than one hump in it. The form that answers what a `<BoxPlot>` structurally cannot: two clusters, a hard floor, a pile-up at a timeout value. Reach for the box plot when several distributions have to be compared side by side, and for this one when a single distribution has to be understood. **A histogram's shape is a property of its bin width, not only of its data.** The same numbers cut into eight buckets and into eighty are two different pictures, and a gap between two humps can be created or erased by moving a bin edge. That is not a defect to be fixed, it is what binning IS, and the defence is to say which rule drew the picture — the default here is Freedman–Diaconis — and to look at more than one width before believing a feature. Uneven buckets add a second trap: under `frequency` a bucket twice as wide stands twice as tall at the same underlying rate, which is what `mode="density"` exists to correct. Recharts earns its place here for the axes, the grid and the tooltip, but not for the bars: a bar chart's bars are positioned by CATEGORY and a histogram's are positioned and SIZED by a continuous measurement. So the x axis is numeric and each bar is drawn from its own two edges — which is what lets an uneven bucket be as wide as it really is instead of being flattened into an equal slot beside its neighbours.

### Props

- `config` — `ChartConfig` default `DEFAULT_CONFIG`. The single series — its label and its paint. Only the FIRST entry is read; a histogram has one distribution and as many bars as it has buckets.
- `values` — `number[]`. The raw observations, in any order. Binned for you by `bins`. Give this OR `data`, not both — `data` wins if both arrive.
- `data` — `HistogramBin[]`. Buckets that were counted somewhere else — by a database, by a sketch, by a metrics backend that only ever ships histograms. Uneven bucket widths are drawn at their real widths, which is the whole reason this takes edges rather than labels. Set `mode="density"` when they are uneven, or the wide buckets will read as tall ones.
- `bins` — `number | number[]`. How to cut `values` up: a number of equal-width bins, or the explicit edges. Defaults to Freedman–Diaconis (`2 × IQR × n^(-1/3)`), falling back to Sturges when the interquartile range is zero. Explicit edges are a RANGE as well as a set of buckets: an observation outside the first and last edge has no bucket to fall in. It is counted anyway — into the tooltip's share-of-total, and into a "Below" or "Above" row in the table — so a tail your edges cut off is still somewhere a reader can find it.
- `mode` — `HistogramMode` default `'frequency'`. What the bar heights mean. `frequency` is the count in each bucket and is what a reader assumes. `density` is `count / (n × bin width)`, so the total area is 1 — which is what makes two histograms of different sample sizes comparable, and what makes uneven buckets honest.
- `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 `<Histogram.Bars>`.
- `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.
- `formatValue` — `(value: number) => string` default `defaultTick`. Formats the measured values — bin edges, ticks, the tooltip's heading.
- `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 { Histogram } from '@misoto22/folio/charts'

<Histogram
  title="Request duration"
  showTitle
  description="Binned by Freedman–Diaconis"
  values={samples}
>
  <Histogram.Grid />
  <Histogram.XAxis label="ms" />
  <Histogram.YAxis />
  <Histogram.Tooltip />
  <Histogram.Bars />
</Histogram>
```

## Example — bin width

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { Histogram } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={choice}
    onValueChange={(next) => next && setChoice(next as (typeof WIDTHS)[number])}
    aria-label="Bin count"
  >
    {WIDTHS.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option === 'auto' ? 'auto' : `${option} bins`}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <Histogram
    title={`Request duration — ${choice === 'auto' ? 'Freedman–Diaconis' : `${choice} bins`}`}
    values={samples}
    bins={choice === 'auto' ? undefined : Number(choice)}
  >
    <Histogram.Grid />
    <Histogram.XAxis label="ms" />
    <Histogram.YAxis />
    <Histogram.Tooltip />
    <Histogram.Bars />
  </Histogram>
</div>
```

## Example — density

```tsx
import { ToggleGroup, ToggleGroupItem } from '@misoto22/folio'
import { Histogram, type HistogramBin, type HistogramMode } from '@misoto22/folio/charts'

<div className="flex w-full flex-col gap-4">
  <ToggleGroup
    type="single"
    value={mode}
    onValueChange={(next) => next && setMode(next as HistogramMode)}
    aria-label="Bar height"
  >
    {MODES.map((option) => (
      <ToggleGroupItem key={option} value={option}>
        {option}
      </ToggleGroupItem>
    ))}
  </ToggleGroup>

  <Histogram title={`Request duration — ${mode}`} data={buckets} mode={mode}>
    <Histogram.Grid />
    <Histogram.XAxis label="ms" />
    <Histogram.YAxis />
    <Histogram.Tooltip />
    <Histogram.Bars />
  </Histogram>
</div>
```
