Skip to content

Charts

Histogram

A numeric distribution grouped into adjacent equal-width ranges.

When to use it: Use it to show shape, concentration, spread, and outliers in continuous observations.

On this page

Example

Loading example…
Histogram.tsxtsx
import {
  ChartDescription,
  ChartTable,
  ChartTitle,
  ChartTooltip,
  Histogram,
  HistogramBin,
  HistogramPlot,
} from "@comp0/react";

const responseTimes = [180, 210, 230, 250, 270, 290, 310, 340, 380, 420, 480, 620] as const;

const formatMilliseconds = (value: number) => `${Math.round(value)} ms`;

export function Example() {
  return (
    <Histogram
      values={responseTimes}
      valueLabel="Response time"
      frequencyLabel="Requests"
      formatValue={formatMilliseconds}
      className="w-full max-w-2xl rounded-lg has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-4 has-[:focus-visible]:outline-teal-600 dark:has-[:focus-visible]:outline-teal-400"
    >
      <ChartTitle className="text-base font-semibold text-zinc-950 dark:text-zinc-50">
        API response-time distribution
      </ChartTitle>
      <HistogramPlot
        aria-label="Histogram showing API response-time distribution"
        binCount={5}
        className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
      >
        {(bin) => (
          <HistogramBin key={bin.index} bin={bin} className="group outline-none">
            <rect
              x={bin.x}
              y={bin.y}
              width={bin.width}
              height={bin.height}
              className="fill-teal-600 stroke-white group-data-active:stroke-zinc-950 dark:fill-teal-400 dark:stroke-zinc-950 dark:group-data-active:stroke-white"
              strokeWidth="1"
              vectorEffect="non-scaling-stroke"
            />
          </HistogramBin>
        )}
      </HistogramPlot>
      <ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
        Most sampled requests completed below 350 milliseconds, with one slower outlier.
      </ChartDescription>
      <ChartTable className="mt-4 w-full border-collapse text-left text-sm [&_td]:border-t [&_td]:border-zinc-200 [&_td]:py-2 [&_th]:border-zinc-200 [&_th]:py-2 dark:[&_td]:border-zinc-800 dark:[&_th]:border-zinc-800">
        <caption className="sr-only">Sampled API response times</caption>
        <thead>
          <tr>
            <th scope="col">Sample</th>
            <th scope="col">Response time</th>
          </tr>
        </thead>
        <tbody>
          {responseTimes.map((responseTime, index) => (
            <tr key={`${responseTime}-${index}`}>
              <th scope="row">{index + 1}</th>
              <td>{formatMilliseconds(responseTime)}</td>
            </tr>
          ))}
        </tbody>
      </ChartTable>
      <ChartTooltip className="pointer-events-none z-50 rounded-md bg-zinc-950 px-2 py-1 text-sm text-white shadow-lg dark:bg-zinc-50 dark:text-zinc-950" />
    </Histogram>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Histogram

    Native figure sharing observations, labels, and value formatting. Owns a DOM element.

  2. ChartTitle

    Native figcaption that visibly names the figure. Owns a DOM element.

  3. HistogramPlot / HistogramBin

    Adjacent SVG bins positioned against value and frequency axes. Owns a DOM element.

  4. ChartDescription

    Visible prose summarizing shape and outliers. Owns a DOM element.

  5. ChartTable

    Native table containing underlying observations or exact bin counts. Owns a DOM element.

  6. ChartTooltipOptional

    Optional floating range and count shown on hover or focus. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Start Histogram with finite observations and visible value and frequency labels.

  2. 2

    Add the supporting parts

    Choose a meaningful binCount and wrap each rendered range in HistogramBin.

  3. 3

    Make the behavior clear

    Describe the distribution and keep the underlying observations or bin counts in a native ChartTable.

    Exampletsx
    <Histogram values={responseTimes} valueLabel="Response time" frequencyLabel="Requests">
      <ChartTitle>Response-time distribution</ChartTitle>
      <HistogramPlot aria-label="Histogram of response times">
        {(bin) => (
          <HistogramBin bin={bin}>
            <rect x={bin.x} y={bin.y} width={bin.width} height={bin.height} />
          </HistogramBin>
        )}
      </HistogramPlot>
      <ChartTable>
        <caption>Response times</caption>
      </ChartTable>
      <ChartTooltip />
    </Histogram>;

Keyboard

Enters the chart at its current bin and leaves with one more Tab.
Moves between adjacent bins.
HomeEnd
Moves to the first or last bin.
Esc
Dismisses an open ChartTooltip.

Forms and accessibility

Charts are descriptive content and do not create form values.

Accessibility checklist

  • Name both the measured value axis and the frequency axis.
  • Choose bins that communicate the distribution honestly; changing bin count can materially change its appearance.
  • Wrap each custom bar in HistogramBin so its range and count are keyboard reachable.
  • Keep the underlying observations or exact bin counts available in a native table.
  • Adjacent bins should remain visibly separable in every color scheme.

API reference

Importtsx
import { ChartDescription, ChartTable, ChartTitle, ChartTooltip, Histogram, HistogramBin, HistogramPlot } from "@comp0/react";

Histogram

DOM element

Native figure sharing observations, labels, and value formatting.

PropTypeDescription
valuesreadonly number[]Finite observations grouped into ranges by HistogramPlot.
valueLabelstringVisible headings for values and counts.
frequencyLabelstringVisible headings for values and counts.
formatValue(value: number) => stringFormats range boundaries and horizontal ticks.

ChartTitle

DOM element

Native figcaption that visibly names the figure.

HistogramPlot / HistogramBin

DOM element

Adjacent SVG bins positioned against value and frequency axes.

PropTypeDescription
aria-labelstringConcise text alternative naming the distribution.
binCountnumberPositive number of equal-width bins; defaults from the observation count.
xMinnumberOptional value bounds and visible tick counts.
xMaxnumberOptional value bounds and visible tick counts.
xTickCountnumberOptional value bounds and visible tick counts.
yTickCountnumberOptional value bounds and visible tick counts.
children(bin: HistogramBinState) => ReactNodeCustom bin renderer receiving its range, count, and geometry.
binHistogramBinStateBin state passed from the plot to HistogramBin.

ChartDescription

DOM element

Visible prose summarizing shape and outliers.

ChartTable

DOM element

Native table containing underlying observations or exact bin counts.

ChartTooltip

OptionalDOM element

Optional floating range and count shown on hover or focus.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

HistogramPlot / HistogramBin

Style hookMeaning
[data-count]The number of observations in this bin.
[data-active]The bin currently reached by pointer or keyboard.

ChartTooltip

Style hookMeaning
[data-open]A value tooltip is visible.

Keep exploring