geom.tile() draws a rectangle that fills its cell on both axes. A bar encodes its value as a length; a tile encodes it as color. The grid comes from a band on x and a band on y, and the value maps to color through a ramp. Use it for a matrix read at a glance: cohort retention, revenue by product and region, activity by day and hour.
Basic example
Three columns: one per axis, one for the value. Both position scales are bands, and the value’s scale is a continuous color ramp:geom.tile() takes no params. The two bands a tile sits in decide its geometry; corner rounding and
the hover outline live on style.geom.tile. See
What the geom decides for the defaults it brings.
Data shape
A heatmap wants long data: one row per cell, with both coordinates and the value. Matrix data usually arrives wide, with one data column per column of the grid. Pipetransform.reshape in front of the mapping and map the columns it produces:
Color is the encoding
color is required on a tile layer, and its scale is what makes the grid readable. With no color scale in the spec, the engine infers one from the column: a numeric column gets the brand sequential ramp, and a categorical one gets the ordinal palette. The second is the waffle case, where a cell names a category rather than measuring one. Declare the scale to pick the ramp yourself:
domainMid. Without the pin, the neutral color sits at the data’s midpoint, which is rarely zero, and the chart reads as though the break-even point moved:
domainMid also turns on symmetric, so a +8% and a −8% cell get equal color intensity. Continuous color covers the full option set: schemes, explicit ramps and interpolation spaces.
Gaps stay gaps
Only the rows you supply paint a cell. A missing combination leaves a hole rather than a cell at the ramp’s low end, so “absent” and “zero” stay distinct. A hole answers no hover either, because the hit-test index drops the same cells the renderer skips:domain on the scale to force the full set of bands: scale.x.discrete({ domain: ['Q1', 'Q2', 'Q3', 'Q4'] }).
Value labels
Data labels are on by default on a tile: a heatmap is read cell by cell, so the number belongs in the cell. Each label centers in its tile and is dropped when the cell is too small to hold it. The text color flips between dark and light to suit the fill under it, so labels stay legible at both ends of the ramp. Turn them off for a chart meant to be read as a texture:What the geom decides
A tile carries defaults that a bar or a line wouldn’t want, so a heatmap looks right before you configure anything:Hover, highlights and annotations
Hover hit-tests the cell the cursor is inside: the full band, including the inset around the painted tile, so the whole grid is live. The hovered cell gains an outline. The tooltip heading is the cell’sx value, and its one row is the value the color encodes.
Highlights work as they do everywhere: a predicate over the post-transform columns. Matched cells stay vivid while the rest step back:
anchorValue plus the group is enough; on a grid, anchorValue names a whole column, so crossValue names the band that picks one cell out of it:
color value, not a length. A sticker centers on the cell too, so turn the cell labels off where the two would overlap.
The kinds that take a panel anchor (text, arrows, shapes) float over the grid in fractions of the plot rect, free of any cell. Give the text a filled box: a ramp runs light to dark under it, so bare text is unreadable at one end or the other. The box comes from the stylesheet, scoped to the annotation by its id:
Limits
- Cartesian only.
coord.flip()andcoord.polar()reject a tile layer with anUNSUPPORTED_COORDerror. A heatmap has no orientation to flip: swap the two mappings instead. identityposition only.'stack','dodge'and'fill'raiseUNSUPPORTED_POSITION; none of them means anything without a value axis.
Related
- Scales — color ramps, schemes and diverging midpoints
- Transforms — reshaping wide matrix data to long
- Data labels — the in-cell value labels
- Highlights — emphasizing the cells a predicate matches
- Annotations — pinning a callout to one cell

