Skip to content
microcharts
ReferenceCoreHeatCell

HeatCell

HeatCell renders one value as a quantized intensity cell for grids you lay out yourself.

encodes
discrete color step
precision
low — use MiniBar/DotPlot for precise comparison
nodes
≤ 2 (cell + optional value label)

You lay out the grid — a matrix of tenants × hours, a custom calendar, a density chip in a sentence — and HeatCell is the single cell inside it, showing how intense one value is against a known scale. Color is quantized into five discrete steps, the most a reader reliably distinguishes at cell size and the same ramp ActivityGrid uses, so intensity means one thing across the library. Continuous opacity would fake precision a 12-px cell can't deliver.

HeatCell
interactive · 3.46 kB · static · 2.11 kB

Install

Import & usereact/heat-cell
import { HeatCell } from "@microcharts/react/heat-cell";<HeatCell value={42} domain={[0, 100]} title="Load" />
Needs package + stylesheetSet up with AI

Try it

When to use it

Use it for table-cell matrices, custom grids, and intensity chips beside labels. For precise comparison use MiniBar or DotPlot; for a time series, HeatStrip or ActivityGrid.

The shared-domain rule

A lone cell has no data to auto-scale from, so domain defaults to [0, 1] — pass your grid's real scale. Every cell in one grid must share one domain: per-cell auto-scaling would make the brightest hour of a quiet tenant look like the brightest hour of a loud one.

// one domain, computed once, shared by every cell
const domain: [number, number] = [0, Math.max(...allValues)];
rows.map((r) => r.hours.map((v) => <HeatCell value={v} domain={domain} />));

Sizing

Variants

shapes
value chip

Edge cases

non-finite value — a designed empty track
value at the domain floor and ceiling — clamped, not rescaled

Values outside the domain clamp to the end steps and are never silently rescaled, so one out-of-range cell can't restretch a grid. A zero-width domain renders the single mid step and logs a dev warning.

Four homes

Accessibility

The accessible name carries the value and its calibration: the shape cells above read "70 — level 4 of 5." The color scale is never the only channel. Non-finite input renders a designed empty track and says "No data." The interactive entry reveals the same reading on hover and focus, with ActivityGrid announcement parity.

This chart is a single unit, so there is nothing to rove between: a click, tap, Enter or Space selects it and fires onSelect, and no selection stays pinned. That is the scalar half of the shared interaction contract. Hover or focus also reveals the reading itself in a floating chip, for the sizes and label modes where the mark does not print it; readout={false} drops the chip and keeps everything else.

Props

PropTypeNotes
value*numberThe value to calibrate.
domain[number, number]Calibration scale — defaults to [0, 1]; every cell in a grid must share one.
stepsnumberDiscrete perceptual steps (default 5, shared with ActivityGrid).
shape"square" | "round" | "dot"Shared cell vocabulary.
label"value" | "none"Centered number when the cell doubles as a chip.
animatebooleaninteractiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts