Heatmap Chart

A matrix heatmap — one hue, magnitude encoded as a quantized opacity ramp, with band scales on both axes, a value-scale legend, ring-on-hover emphasis, and a loading state.

LessMore

Installation

pnpm add @blakeui/pro-react
npm install @blakeui/pro-react
yarn add @blakeui/pro-react
bun add @blakeui/pro-react

Usage

HeatmapChart is built on visx band scales and the motion library. It is a single component: pass data as one { x, y, value } cell per position. Columns and rows are derived from the distinct x and y keys (or pinned with xDomain / yDomain), and both axes are band scales — the first chart in the library on bands in two dimensions. Cells are square-locked: the cell size falls out of the available width, and the chart's height follows from it.

Magnitude is encoded as opacity on a single hue — every cell is painted in color (--chart-1 by default) and only its intensity changes, quantized into levels steps. Chart chrome (axis labels, the legend hairline) maps to the neutral tokens.

import {HeatmapChart} from "@blakeui/pro-react";

const activity = [
  {x: 0, y: "Mon", value: 4},
  {x: 0, y: "Tue", value: 1},
  // …
];

function App() {
  return <HeatmapChart data={activity} valueLabel="Commits" />;
}

Color

The ramp is deliberately single-hue. Because opacity carries the value, the chart never dims non-hovered cells the way the other charts in this library do — a dimmed high-value cell would be indistinguishable from an undimmed low-value one. Instead, hovering emphasises only the active cell, with a stroke ring and a slight scale, and leaves every other cell exactly as it was.

The lowest step sits at 0.10 rather than 0 so a zero-value cell stays visible against the card in both light and dark. Override the whole ramp with levelOpacities, and the bucket boundaries with thresholds.

Motion

On mount every cell fades from transparent to its encoded opacity over animationDuration on a back-loaded curve — by default in unison, so the grid resolves as one field. Set enterStagger above 0 to spread the fades diagonally into a dissolve instead. The encoded magnitude is a static fillOpacity on each cell, kept deliberately separate from the animated group opacity, so the entrance can never overwrite the data. All motion collapses to an instant render under prefers-reduced-motion, and the chart exposes the active cell to assistive tech through a polite live region (arrow keys move in two dimensions; Home/End jump along the row; Escape clears).

Loading state

Set status="loading" to show a stroke-only skeleton — the cell outlines with a shimmer sweeping through them — while data resolves; flip it to "ready" and the load→ready choreography plays.

Loading activity…
LessMore

API Reference

HeatmapChart

The single chart component. Measures its container, derives band scales on both axes, quantizes the values onto the opacity ramp, and renders the cells, hover layer, legend, and loading chrome.

PropTypeDefaultDescription
dataHeatmapChartDataPoint[]One cell per position: {x, y, value}. Combinations absent from the array are simply not drawn.
xDomain(string | number)[]Explicit column order. Defaults to each distinct x in first-appearance order.
yDomain(string | number)[]Explicit row order. Defaults to each distinct y in first-appearance order.
xTickFormat(key, index) => stringColumn label. Return "" to omit one — how sparse, month-style labelling is done. Labels that would collide are culled automatically.
yTickFormat(key, index) => stringRow label. Return an empty string to omit one.
yTickFilter"all" | "odd" | "even""all"Which row labels render — alternate them when the rows are tighter than the type.
marginPartial<Margin>{top: 28, right: 16, bottom: 0, left: 40}Plot margins — room above for column labels, a left gutter for row labels.
heightnumberFixed pixel height. By default the height is derived from the square cell size.
cellSizenumberFixed cell size in px. Overrides the width-derived square.
gapnumber2Gap between cells in px — absolute, not a band-padding ratio.
cornerRadiusnumber2Cell corner radius in px.
colorstring"var(--chart-1)"The single hue every cell is painted in; magnitude rides the opacity ramp.
levelsnumber5Number of quantized steps in the ramp.
levelOpacitiesnumber[][0.1, 0.32, 0.55, 0.78, 1]Opacity per level, low→high. The 0.10 floor keeps a zero-value cell visible against the card in both themes.
thresholdsnumber[]Explicit inclusive upper bounds per level (length levels - 1). Defaults to a linear quantization of [0, max(value)].
animationDurationnumber1600Entrance duration in ms.
enterStaggernumber0Per-cell stagger as a fraction of the entrance window (0–1). 0 fades the grid in unison.
showXAxisbooleantrueRender the column (x) axis labels.
showYAxisbooleantrueRender the row (y) axis labels.
showLegendbooleantrueRender the value-scale legend below the plot.
legendLessLabelstring"Less"Label at the low end of the legend ramp.
legendMoreLabelstring"More"Label at the high end of the legend ramp.
showTooltipbooleantrueShow the hover tooltip layer and the ring-on-hover emphasis.
valueLabelstring"Value"Name shown against the value in the tooltip.
valuePrefixstringStatic text rendered before the tooltip value (e.g. "$").
status"loading" | "ready""ready"While loading, shows the skeleton grid. Flipping to ready plays the entrance.
loadingLabelstring""Centered caption shown while loading. Empty string hides it.

Also supports all native div HTML attributes.

HeatmapChartDataPoint

Each entry in data describes one cell of the matrix.

FieldTypeDescription
xstring | numberColumn key. Distinct values become the columns, in first-appearance order.
ystring | numberRow key. Distinct values become the rows, in first-appearance order.
valuenumberMagnitude driving the opacity ramp. Values at or below 0 land on level 0 — drawn as a real cell at the floor opacity, not omitted.

On this page