Skip to main content
Slots replace how a single region of the chart renders. The spec still owns whether that region exists and what data it receives, and your override gets the same render-ready props the default would. Pass them through the slots prop.

Regions

EditorSurface defaults to a no-op, so a read-only embed bundles no editing code; @graphysdk/react/editable is what fills it. A chart that only paints leaves it alone.
Custom Footer / Header slots receive a brandMark visual ('full' | 'mini' | 'hidden'), resolved from config.content.brandMark and the frame size. That prop is not the visibility toggle; the toggle is brandMark.enabled. source remains your data-source attribution. The default badge paints inside the matching region when placement matches.

Three kinds of slot

How a region earns its space decides what you hand the slot. The type reflects it: the first two kinds are bare components, and layout-coupled slots are a SlotOverride object.

Box-filling — Tooltip, Grid, Swatch, EditorSurface

These paint inside a box the layout already sized, or float free of it. Provide a component and nothing else:
Rows follow legend order. The hovered row is the one with primary set, not the first. The tooltip’s positioning stays built in: you own the body, not the anchor. These reserve their own space, and the layout measures the rendered DOM to find out how much. Both prop types carry a ref. Forward it to your outer element, or the region reserves zero height and the plot paints over it.

Layout-coupled — Legend, Headline, AxisTicks, AxisLabel

These reserve an edge band the layout sizes up front, so they are a SlotOverride: a render component and a measure that reports the region’s size. Paint and reserved space must agree, so the layout asks measure how much room to leave:
Legend, AxisTicks and AxisLabel take a (region, ctx) => number: the band’s thickness in pixels, a height on the top and bottom edges and a width on the left and right. Headline takes a HeadlineMeasurer instead: an object with measureHeadline(headline, size, inDonutHole?) returning a { width, height } box and measureHeadlineItemWidths(headline, size) returning each strip item’s width.
The second argument to the function-shaped measures is a SlotMeasureContext: measureText(text, { family, size }), the same Canvas-backed measurer the built-in regions use, taking a final pixel size, and the active textScale to multiply an em size by. Sizing from those keeps your reserved band matched to what paints.
Give a layout-coupled slot’s measure a stable reference (define it outside render or memoize it). The layout reads overrides through a ref, so an inline measure still takes effect on the next paint, but that paint doesn’t retrigger layout, so paint and reserved space drift apart.
Two of the measures are skipped where the band is empty: AxisTicks’s reserves 0 for an axis with no tick band, and Legend’s reserves 0 for an empty legend. Your override isn’t consulted in those cases. AxisLabel’s has no such gate.

Pressable guides in point-and-edit

A press on a tick label, an axis title, a legend pill, a direct label or a grid line selects its axis, legend or grid lines through attributes on the node that paints it. A custom Grid, AxisTicks, AxisLabel or Legend joins by spreading the same attributes, only where useGuidesTakePress() is true:
  • Tick labels spread stampAxisEditTarget(scaleAestheticKey, tickIndex), where tickIndex counts the axis’s ticks in formattedAxes, so a double click on a band axis’s tick takes that band.
  • Axis titles spread stampAxisEditTarget(scaleAestheticKey), with no tick.
  • Legend pills and direct labels spread stampLegendEditTarget({ legendIndex, itemIndex }), counting formattedLegends and that legend’s items, so a double click takes the item’s group.
  • Grid lines spread stampGridEditTarget(scaleAestheticKey) on a group holding the stroke and a hit area of your own, such as a transparent stroke a few pixels wide with pointer-events="stroke". The capture rect over the panel takes the event, so the editor reads the element stack under it and finds the stamp there.
The attributes go on the element holding the paint and whatever area should take the press, and that element must take pointer events. A node without them takes no press, and a click there selects the chart. A grid line your slot hides or moves takes its hit area with it: nothing else answers for it. A grid line in a custom Grid, with its own hit stroke:

Slot props

Every props type is exported from @graphysdk/react, along with DefaultHeader, DefaultFooter, DefaultTooltip, DefaultGrid and DefaultSwatch to wrap or delegate to. The rects a slot receives are SVG-local for the in-panel regions (Grid, AxisTicks, AxisLabel) and layout rects for the rest. A Swatch gets color as a plain string and, when the entry paints a gradient or pattern, the full paint beside it. mainAxis is the chart’s category axis, 'y' under coord.flip(), so an interval swatch can lie along the other one.
A slot’s measure lands on the layout measurer under its own name: AxisTicks.measure becomes measureAxis, AxisLabel.measure becomes measureAxisLabel, and Headline.measure’s two methods keep theirs. That is the SlotMeasureOverrides shape in the layout types.
  • Geom renderers — replace a whole geom’s paint, not just a region
  • Interactivity — the Tooltip slot in context
  • Theming — restyle regions with tokens before reaching for a slot