Skip to content
Extend the engine C4 4 min read Customisation

Custom overlays (advanced)

Per-element on-canvas affordances — resize, crop, drag handles.

Overlays are the deepest, rarest extensibility surface. Most BuilderJS projects ship without ever authoring one. Read this only if your custom Element needs on-canvas affordances the engine's stock overlays don't cover — resize handles for a custom dimensional element, a crop UI for a custom image-like element, drag handles, info popovers anchored to specific corners.

What an Overlay IS

An Overlay is per-element on-canvas chrome: visible UI affordances that float over (or alongside) the rendered element when it's selected. The shipped engine ships overlays for ImageElement (crop / upload / corner-radius), GridElement (column-resize handles), and the universal selection-frame (the bounding rectangle around the active element).

Where Controls modify settings via the right-rail panel, Overlays modify settings directly on the canvas — drag a corner of an image to crop, drag a column edge to resize, drag the selection frame's bottom-right handle to scale.

When to author one

Most domain-specific elements don't need an overlay. Three signals you do:

  1. The buyer's mental model is "I edit this on the canvas, not in the panel" — e.g. a Map element where pan / zoom should be on-canvas.
  2. The element has a dimensional property the buyer wants to tweak by feel — e.g. an embed's iframe height, a chart's bar count.
  3. The element has a rare interaction not covered by any shipped Control — e.g. a polygon vertex editor, a colour-stop gradient editor.

If your element doesn't match these signals, ship it with getControls() only and skip this doc. Most buyers do.

The contract

getOverlays() is the sibling of getControls() — same calling convention, different purpose:

Each Overlay subclasses `BaseOverlay` (or one of the shipped overlays). The engine pipes the active element through every Overlay's render hook when selection changes; the Overlay paints its chrome onto the canvas above the element.

The reference reading

Authoring an Overlay isn't a 1500-word walkthrough — it's a careful read of the canonical spec plus the shipped Overlays as reference. Two stops:

  1. The canonical specdocs/core/OVERLAY.md. 2900 LOC. Read §3 (BaseOverlay class spec) + §6 (authoring tutorial). The rest is engine-internal architecture; you don't need it as a buyer.

  2. The shipped Overlays — under src/includes/<Name>Element.js look for getOverlays() overrides. The richest example is ImageElement (crop overlay + upload overlay + corner-radius overlay). GridElement has the column-resize overlay. Each Overlay class is in its own file (ImageCropOverlay.js, GridResizeOverlay.js, …) — read it line by line.

A starter shape

For when you've decided to author one. The legal-minimum subclass:

The engine handles bounding-rect tracking + selection state for you; the Overlay only owns its own chrome + the interactions it surfaces.

Why this doc is short

Authoring an Overlay is engine-source-deep work. The pattern doesn't compress to a 30-minute walkthrough — every overlay you author is closely coupled to the element's geometry + the canvas's coordinate space. Going deeper here would either be (a) repeating OVERLAY.md (which is already canonical) or (b) papering over the real complexity.

If you're committed to authoring an Overlay, allocate a half-day, read the source carefully, and copy a shipped Overlay as your starting point.

Did you make it?

If you're past this page without authoring an Overlay, you've made the right call. Most buyer projects ship the engine plus one or two custom Elements + Controls and never touch this surface.

If you DO need to author one:

  • ✅ Read docs/core/OVERLAY.md §3 + §6 in full.
  • ✅ Identify the shipped Overlay closest to your need (ImageCropOverlay, GridResizeOverlay, BlockDragHandleOverlay).
  • ✅ Copy that file → rename → tweak. Don't author from scratch.
  • ✅ Test: select your element, see the Overlay paint, interact with it, watch notifySyncListeners() repaint the canvas.

What's next?

  • Wire a real backend — the bigger leap: production save / load / auth / multi-tenant.
  • 🔗 docs/core/OVERLAY.md — the canonical spec.
  • 🔗 src/includes/ImageElement.jsgetOverlays() returning crop + upload + corner-radius overlays.

Stuck?

  • 🐛 file an issue with: your element class, the Overlay subclass attempt, the canvas-coordinate behaviour you expected vs got.
  • 🐛 the Overlay layer is one of the densest parts of the engine; expect a learning curve. The shipped overlays are battle-tested across Phase-3 — they're your reference, not just inspiration.