(geom, coord) composition — the render half of a geom, opposite its compile half. Renderers paint observations and respond to hover; they don’t own layout, guides, or data-label placement.
The registry is keyed on the (geom, coord) pair, so bar × cartesian (a column) and bar × polar (a pie wedge) are independent renderers that share no code. Adding one never touches the other.
defineGeomRenderer is dual-target
The same function binds a renderer two ways, depending on what you pass first:
A whole custom geom
Pass aGeom instance to pair both halves. The result carries the compile definition on .definition, so dropping it into plugins registers the geom and derives its typed builder method:
Render-only overrides
Pass a built-in geom name to rebind only the paint half. The built-in compile half keeps running — positions, stacking, scales, and axes are unchanged — so only the look differs. This is how you restyle bars without reinventing bar geometry:The contract
Every renderer implements aGeomRenderContract:
The coordinate system this contract paints under —
'cartesian', 'flip', or
'polar'. A geom binds one contract per coord.Paints the observations. Receives
{ layer, coordSystem, isAnimated, formattingLocale }. The { fn, options: { overlay: true } }
form paints into a screen-aligned portal instead — see Live geoms.Paints the hover state for this layer’s observations. Receives the hovered
primary hit plus its group and related hits, the coordSystem, and the
panelRect.Cross-layer hints — e.g. dots dropped on a line at the hovered x. Return
null to opt out.The positional guide drawn when this is the hovered layer:
'band' shades the
hovered category (bars), 'crosshair' draws a rule at the hovered value
(lines, areas). Omit or pass null to draw none (scatter points).The swatch shape shown for this geom in the legend and tooltip.
Optional repaint of the matched subset for the
highlight overlay. Falls back to
render
when omitted. Override it when the plain grouped render would misrepresent a
matched subset.A per-cursor spatial query for a layout geom.
Only consulted when the compile half declares
spatialKind: 'render-hit-test'.Reading scaled positions
The renderer reads coordinates off eachObservation with the value readers — it never re-derives a position. The compiler already scaled them; the reader hands you the resolved value, and helpers convert it to the paint frame:
null for a missing or unscaled value — guard for it and skip the observation rather than painting NaN. For a render-only override of a built-in geom, higher-level readers like getBarRectBounds(mainAxis, observation) hand you the geom’s normalized [0,1] bounds directly.
Hover
Hover has two render entry points, both in the contract:renderHoverrepaints this layer’s hovered observation — usually the same one drawn bolder or haloed. Because positions come from the compiled observation, the highlight lands exactly over the base shape.renderHoverCompanionspaints hints on other layers at the hovered x. Returnnullwhen the observation is its own highlight.
Hit-testing layout geoms
Most geoms are hit-tested from their scaled positions — the compiler builds the index fromspatialKind. But a layout geom computes its geometry with an algorithm render-side (Sankey ribbons, Treemap tiles, Voronoi cells), so the compiler can’t see the shapes to index them.
Such a geom declares spatialKind: 'render-hit-test' on its compile half and supplies a hitTest factory on the contract. The factory runs once per data change and returns a per-cursor tester; the cursor arrives in panel-local [0,1] (top-left origin), and the tester returns the identity key of the observation under it, or null:
useGeomHitTest is the underlying hook, but the contract’s hitTest is the path you write against.)
Live and draggable geoms
A geom that runs its own simulation or drag interaction — a force-directed graph — must own its pointer events. For that,render takes the overlay-hosted form: { fn, options: { overlay: true } }. Its function receives input.overlay with a pushHover to feed the central hover store and the panelRect for placement:
useGeomHover(layerId) is the lower-level push hook the overlay form wraps — reach for it only when the overlay form doesn’t cover your case.
Related
- Custom geoms — the compile half a renderer pairs with
- Interactivity — the hover model renderers plug into
- Slots — replace a region’s render without writing a geom

