Heatmap
PreviewSVGReads selection
Shows a value across two categorical dimensions, such as orders by weekday and hour, on a sequential color ramp.
Playground
Orders by hour
Sep 7 – Oct 6, 2026 · local time
Orders by Weekday and Hour, 7 × 24 cells. Highest Tue 18:00 (69); lowest Mon 01:00 (1).
| Weekday | 00:00 | 01:00 | 02:00 | 03:00 | 04:00 | 05:00 | 06:00 | 07:00 | 08:00 | 09:00 | 10:00 | 11:00 | 12:00 | 13:00 | 14:00 | 15:00 | 16:00 | 17:00 | 18:00 | 19:00 | 20:00 | 21:00 | 22:00 | 23:00 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Mon | 3 | 1 | 7 | 8 | 4 | 6 | 10 | 10 | 14 | 15 | 19 | 32 | 26 | 26 | 43 | 27 | 42 | 39 | 44 | 46 | 58 | 30 | 24 | 21 |
| Tue | 8 | 4 | 4 | 1 | 8 | 5 | 6 | 8 | 13 | 12 | 27 | 22 | 33 | 32 | 40 | 30 | 33 | 45 | 69 | 50 | 42 | 33 | 46 | 24 |
| Wed | 8 | 5 | 2 | 4 | 3 | 6 | 3 | 1 | 9 | 17 | 19 | 27 | 22 | 26 | 36 | 24 | 27 | 45 | 35 | 40 | 23 | 38 | 26 | 21 |
| Thu | 3 | 2 | 5 | 6 | 9 | — | 4 | 7 | 8 | 11 | 28 | 16 | 18 | 27 | 23 | 28 | 16 | 38 | 38 | 44 | 28 | 25 | 23 | 18 |
| Fri | 6 | 3 | 4 | 5 | 4 | 2 | 7 | 5 | 12 | 9 | 19 | 24 | 21 | 21 | 31 | 39 | 21 | 46 | 35 | 38 | 44 | 33 | 18 | 20 |
| Sat | 4 | 7 | 3 | 5 | 4 | 3 | 6 | 6 | 6 | 18 | 31 | 18 | 13 | 25 | 23 | 15 | 17 | 28 | 43 | 26 | 31 | 33 | 14 | 15 |
| Sun | 5 | 9 | 2 | 8 | 2 | 6 | 2 | 5 | 7 | 13 | 10 | 21 | 28 | 18 | 24 | 15 | 25 | 29 | 26 | 39 | 32 | 32 | 20 | 14 |
aggregate
<Heatmap
data={ordersByHour}
x="hour"
y="weekday"
xOrder={HOURS}
yOrder={WEEKDAYS}
xLabels={pad2}
xFormat={(h) => `${pad2(h)}:00`}
countLabel="Orders"
/>Installation
Heatmap is exported from @quartile/react, alongside every other chart and component.
git clone https://github.com/zinnoberHaus/quartileNot on npm yet: build the package from the clone as described in Installation. The preview API may change before publication.
Usage
Pass raw rows: they are aggregated per cell, summed with a value and counted without one. Set xOrder so empty columns still appear.
import { Heatmap } from '@quartile/react';
const HOURS = Array.from({ length: 24 }, (_, h) => h);
const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// orders: { weekday: 'Tue', hour: 19, amount: 128.5 }, …
type Order = { weekday: string; hour: number; amount: number };
export function OrdersByHour({ orders }: { orders: Order[] }) {
return (
<Heatmap
data={orders}
x="hour"
y="weekday"
xOrder={HOURS}
yOrder={WEEKDAYS}
xLabels={(h) => String(h).padStart(2, '0')}
/>
);
}Heatmap reads the nearest
<Selection> and does not publish to it, so it follows brushes and clicks elsewhere. See Linked selections.Props
PropTypeDefaultDescription
datarequiredDataInput<R>—Raw rows (aggregated per cell), or a dataset with a schema attached.
xrequiredkeyof R & string—Field for the columns, e.g. hour of day.
yrequiredkeyof R & string—Field for the rows, e.g. weekday.
valuekeyof R & string—Measure per cell. Counts rows when omitted.
aggregate'count' | 'sum' | 'mean' | 'median' | 'min' | 'max'AggregateOp—How rows in a cell combine. Defaults to sum (or count without
value).formatFormatter—Formats cell values. Defaults to the field's schema format.
xFormatFormatter—Formats column values in the tooltip and peak label.
yFormatFormatter—Formats row values in the tooltip, row labels and peak label.
xLabels(value: unknown, index: number) => string—Short column labels under the grid, e.g.
(h) => String(h).padStart(2, '0'). Thinned to fit.xOrderreadonly unknown[]—Column order, e.g. 0…23 so empty hours still get a column. Defaults to sorted values.
yOrderreadonly unknown[]—Row order, e.g. Mon…Sun. Defaults to first appearance (numbers and dates sorted).
domain[number, number]—Value range the ramp spans. Defaults to zero → maximum.
cellHeightnumber15Cell height in px. Width always fills the container.
countLabelstring'Count'Label for counts in the tooltip when
value is not set.peakbooleantrueShows "peak <row> <column>" under the grid.
legendbooleantrueShows the "Fewer … More" ramp legend.
idstring—Publisher id. Defaults to a generated id.
selectionstring | false—Selection to read from. Defaults to the nearest; false opts out.
Frame and state props shared with the charts (11) · ChartBaseProps, ChartStateProps
PropTypeDefaultDescription
heightnumber—Plot height in px. Width always fills the container.
classNamestring——
styleCSSProperties——
aria-labelstring—Accessible name. A summary of the data is generated and rendered as hidden text inside the figure.
view'chart' | 'table'—
table renders the same data as a table, with the same formats, at the chart's size. Large lazy tables expose a data-view control rather than mounting every hidden row.tooltipNotestring—Plain-text context shown with tooltips and included in the accessible chart description.
loadingboolean | string—Shows the loading state at the chart's size, so the layout never jumps.
errorboolean | string | Error | null—Shows the error state. A string or Error becomes the message.
errorCodestring—Shown in the error state; copyable for support.
onRetry() => void——
empty{ title?: string; description?: string; action?: ReactNode }—Title and description for the empty state. Shown when there are no rows.
Accessibility
Keyboard
Tab into the grid; focus starts on the peak cell. Arrow keys move between cells.
Screen readers
A summary gives the grid size and the highest and lowest cells with their values. The focused cell is announced as you move.
Table fallback
Screen readers always get a table of the plotted values with the same formats.
view="table" shows it at the chart's size.Report an issueBuilt Oct 21, 2026