Geom definition that declares the geom’s aesthetics and places its observations in data space — and a render half that paints them. This page covers the compile half; the geom renderers page covers paint.
The guiding rule: the compiler owns every position, the renderer invents none. Your geom declares which position roles it needs, writes their values in data units, and the compiler scales them onto the axes. An observation then stays anchored through any domain change — zoom, a log scale, a flipped coordinate — because its coordinates were resolved by the same scales as every other geom.
Anatomy of a geom
Extend theGeom class, parameterised by its params type. Here’s a lollipop — a dot on a stem dropped to the baseline:
What the definition declares
The geom’s name, written
as const. It becomes the typed builder method
(kit.geom.lollipop) and the renderer registry key.Default values for the geom’s styling params. The type parameter on
Geom<Params> types both these and the params
the builder accepts.The positions the geom occupies, each
{ axis, role, valueKind }. A 'point' role is a single coordinate on its
axis; 'min' / 'max' form an interval (like a bar or area); 'scalar' is a magnitude. A role may name a custom
positional aes — the lollipop’s max role is exposed as the y aesthetic. Write the array as const so the typed
builder can constrain aes to exactly these aesthetics.The visual and data aesthetics the geom reads beyond position — here,
color. Each is { kind, name }, with kind one
of 'visual' (trained through a visual scale, like color or size) or 'data' (a raw input a layout geom hands to
its algorithm).Which coordinate systems the geom compiles under. The lollipop is
cartesian-only.
How the compiler builds the hover hit-test index from the geom’s scaled
positions.
'buckets' groups by x (lines, areas), 'rects' uses rectangular
bounds (bars), 'points' uses point proximity, 'noop' opts out. Use
'render-hit-test' for a layout
geom whose
geometry the compiler can’t see.The compile step
compile(input) receives the layer’s data (a Dataset) and returns a CompiledGeom — { data, mapping }. This is where the geom writes any positions it derives rather than reads from the mapping. The lollipop’s baseline isn’t in the data, so compile adds it as a constant column in data units; the compiler then scales yMin and yMax through the shared y-scale like any other position.
Do the minimum here: derive positions, and leave scaling, stacking, and axis placement to the pipeline. A geom that computes screen pixels in compile has crossed the compile/render seam.
Registering and using it
The compile half pairs with a render half throughdefineGeomRenderer, and the paired result goes into the plugins array. From there the typed builder method appears, constrained to exactly the aesthetics and params the definition declared:
A native spec doesn’t auto-infer position scales the way the chart-type
recipes do — declare
scale.x() and scale.y() explicitly, or the positions
resolve to NaN. See
Scales.Related
- Geom renderers — the render half,
defineGeomRenderer, hit-testing, hover - How a chart is built — where
compilesits in the pipeline - Scales — how declared positions get trained and mapped

