Skip to main content
Version: 4.3

Overlays

<Overlay /> and <ElementOverlay /> render React content on top of a Paper, kept in sync with paper pan and zoom. Use them whenever you want React UI over the diagram (contextual toolbars, badges, annotations, hover popovers) without baking that UI into a cell's definitions.

Reach for:

  • <Overlay /> when you want React content at a fixed graph coordinate (independent of any element)
  • <ElementOverlay /> when you want React content attached to a specific element, tracking its position, size, and rotation

Both components must be rendered as children of <Paper /> so they can read the paper's transform.

note

Overlays are HTML rendered over the paper, not part of the diagram's SVG — so they never appear in an image export. Content that must survive export belongs in the cell itself (renderElement).

The example below mounts an <ElementOverlay /> on whichever element is selected. Selection state lives in the <Diagram> root (see Selection), so a click handler updates it directly, with no <Selection> component required.

Contextual toolbar on the selected element

Click a card to select it; a status picker floats above the selected card, and choosing a status writes it straight back to the element. A second overlay, pinned to a fixed graph coordinate, shows the instruction. Review starts selected so the picker is visible on first paint.

Clicking a card updates the selection with selectCells; while exactly one element is selected, a small component floats an <ElementOverlay cell={…} position="top" origin="bottom" dy={-12}> above it. Choosing a status calls useGraph().setCellData(...) to write it back, and the picker stays in sync because it reads the element's data with useCell. It all runs off the <Diagram> selection collection through <Paper> event props, with no <Selection> component. The instruction is a second overlay: a <Overlay x={20} y={20}> pinned to a fixed graph coordinate, with its content marked pointer-events: none so it never blocks a click on the canvas. (Set that class on the child you render: <Overlay> forces pointer-events: auto on its own wrapper element.)

<Overlay> and <ElementOverlay> must sit inside a <Paper>, either directly as a child or deeper inside a renderElement callback. Both placements render above the diagram.

Positioning model

position and origin are two halves of one decision. position is the point on the element's bounding box; origin is the point on the overlay that sits on top of it. Both props accept the same nine values, and default to top-left.

The demo below makes the placements concrete. One element sits in the middle; nine <ElementOverlay>s wreath it. Each overlay pairs its position with the opposite origin, so the overlay lands just outside the element in the direction named by position (or on top of the element, when position is center):

Graph coordinates

When the overlay isn't tied to a specific element, use <Overlay /> directly with local (graph) coordinates:

<Overlay x={20} y={20} origin="top-left">
<div>Click any card to edit its status</div>
</Overlay>

x and y are in the same coordinate system as element positions, so the overlay sits at graph (20, 20) regardless of where elements are dragged. Pan the paper and the overlay pans with it. That's what separates a graph-coord overlay from a screen-space tooltip: the annotation belongs to a spot in the diagram.

Pixel-level adjustment

dx and dy shift the overlay by a number of screen pixels after the anchor and position math is done. They don't scale with the paper, so they remain visually consistent at any zoom level. Use them for spacing or fine alignment:

<ElementOverlay cell={targetId} position="top" origin="bottom" dy={-12} />

Following the zoom

By default an overlay keeps its CSS size at every zoom level, so a badge or toolbar stays the same on screen however far you zoom out. Set scaleWithPaper when the overlay should grow and shrink with the diagram instead. Use it for content that belongs inside the drawing itself, like a text annotation that should stay proportional to the elements around it. Set wheelTransparent to let mouse-wheel zoom and pan pass through the overlay to the paper underneath.

Both chips below pin to the same element. Zoom with the controls: the top one has scaleWithPaper and tracks the zoom, while the bottom one holds its size.

Following the rotation

<ElementOverlay /> stays upright by default: it pins to the axis-aligned bounding box of the rotated element, so labels stay level and readable at any angle. Set rotateWithElement when the overlay should turn with the element instead, so it holds the same spot against the element's edges.

The element below starts rotated. Drag its rotate handle: the chip on the right has rotateWithElement and turns with it, while the one on the left stays level.

Explicit angle

<Overlay /> (the graph-coordinate variant) accepts an explicit angle in degrees:

<Overlay x={120} y={40} origin="center" angle={-15}>
<div className="label">Tilted</div>
</Overlay>

Inside renderElement

<ElementOverlay> can render inline inside a renderElement callback. Drop the cell prop, and the cell is taken from the surrounding context:

function TaskElement({ label }) {
return (
<>
<ElementOverlay position="top-right" origin="bottom-right">
<button>Delete</button>
</ElementOverlay>
<HTMLHost>
<span>{label}</span>
</HTMLHost>
</>
);
}

Use this when every element of a given type should carry the same overlay (delete button, edit icon, status badge), so you don't need separate selection plumbing. Don't pass cell here; pick one mode.

<Overlay> works at either mount point unchanged.