Skip to main content
The engine ships with a fixed set of built-in geoms, stats, and transforms — but the grammar is open. You can register your own and they become first-class: a typed builder method, compiled through the same pipeline, painted through the same renderer registry. This is the advanced surface; most charts never need it. There are two ways in. Plugins extend the grammar itself — new geoms, stats, and transforms that compile and render like the built-ins. Slots are lighter: they replace how one region of the chart renders (a header, legend, or tooltip) without touching the grammar. Most of this section is about plugins; slots are a self-contained escape hatch on the renderer.
Before writing a plugin, check whether a render-only override or an existing geom composed differently gets you there. A new geom is the right tool for a genuinely new shape, not a restyle of an existing one.

One array, both halves

Everything you add is registered through a single plugins array. Passing it once seeds two things at once — what you can write (the typed builder methods) and what can compile and render — so the authoring surface and the runtime can never drift apart.
LollipopGeom is the compile half; the object literal is the render contract. A stat or a transform has no render half, so it goes into the same array on its own.

Two entry points

react-renderer
The ergonomic one. Returns the typed builder (geom / stat / transform / scale / coord / createSpec / pipe) and a GraphProvider already bound to the same plugins. Reach for this in a React app.
viz-engine
The primitives createGraphyKit wraps. Use createGraphyBuilder({ plugins }) for the headless authoring surface and pass the same array to <GraphProvider plugins={...}>. Choose this for framework-agnostic or advanced wiring.
Both take the same plugins array. The kit is pure sugar — it calls createGraphyBuilder and pre-binds a provider, nothing more.

What you can add

A geom has two halves because it both places observations (compile) and paints them (render). Stats and transforms only touch data, so they’re compile-only — no renderer. A stat is a Stat subclass, a transform a TransformStrategy object; each goes into plugins on its own and earns a typed builder method (kit.stat.<type>(), kit.transform.<transformType>()) beside the built-in statistics and transforms.

Slots

Replace a region’s render — no plugin, no geom.

Custom geoms

Define a new shape with Geom — position roles, aesthetics, and the compile step.

Geom renderers

defineGeomRenderer, render-only overrides, hit-testing, and hover.

Statistics

The layer-scoped summaries a stat computes — the vocabulary a custom Stat joins.

Transforms

The dataset reshapings a transform applies — the vocabulary a custom TransformStrategy joins.

Diagnostics

A plugin mistake surfaces as a VizDiagnostic rather than a throw, so the chart keeps rendering around it. All but one are warnings, and several are the only signal that an otherwise-silent plugin is broken — read them before reaching for a debugger. Some are computed once, when the provider reads the plugins array; the rest are recomputed per compile, since they depend on what a layer actually compiled to.