0.23: canvas per-item overlay (Duplicate/Delete, overrides.actionBar) never mounts, though selection state updates correctly
Summary
In @puckeditor/[email protected], selecting a component on the canvas correctly updates state.ui.itemSelector / usePuck().selectedItem and correctly updates the right-hand Fields panel — but the canvas's own per-item floating overlay (the one that hosts the native Duplicate/Delete buttons, and anything added via overrides.actionBar) never mounts. No error is thrown; the overlay's container ([data-puck-overlay] in the shipped bundle) simply never appears in the DOM.
Reproduction
Minimal config: a DropZone-based root component (legacy zones, not slots) with a couple of plain, non-resolving child components. No custom overrides, no plugins.
- Render
<Puck config={config} data={data} onPublish={...} />withiframeleft at its default (enabled). - Click any placed component on the canvas.
- Observe:
usePuck().appState.ui.itemSelectorupdates correctly to{index, zone}for the clicked node (verified with a debug hook reading it live).- The Fields panel (right sidebar) correctly re-renders to that component's fields.
- No overlay appears. No Duplicate/Delete buttons, no drag handle chip, nothing —
document.querySelectorAll('[data-puck-overlay]')is0both before and after the click.
Reproduced with:
- Two different documents/templates (different component trees).
- A component with no resolver/binding at all (a plain text/heading-style block) and one with an async data-fetching child — same result either way, ruling out a resolver/Suspense interaction.
- Headless Chromium, headed Chromium under Xvfb, real mouse
hover+move+clicksequences, and a programmaticdispatch({type:"setUi", ui:{itemSelector}})— same result every way. - With
overrides.actionBarentirely absent from the config (so this is not specific to a custom override — Puck's own default Duplicate/Delete overlay has the identical symptom).
What I found tracing the shipped bundle
(Line refs are against the published @puckeditor/[email protected] npm tarball's dist/chunk-*.mjs, which will not match the source tree 1:1 — offered as a lead, not a citation.)
DraggableComponent'sisSelectedis a live store subscription (s.selectedItem?.props.id === componentId) and does flip correctly — confirmed via a debug hook.- The overlay's visibility is local
[isVisible, setIsVisible]state, set inside auseEffectthat is itself wrapped instartTransition:useEffect(() => { startTransition(() => { if (hover || indicativeHover || isSelected) { scheduleSync(); setIsVisible(true); ... } else { setIsVisible(false); } }); }, [hover, indicativeHover, isSelected, iframe]); - The JSX gate for the overlay's
createPortal(...)isdragFinished && isVisible.dragFinisheddefaults totrue(useState(true)), so it should not be the blocker. - Despite
isSelectedbecomingtrue(confirmed),isVisiblenever appears to commit — the overlay is simply absent from the DOM, with no console error/warning.
I was not able to narrow this further without instrumenting the built package itself, but the startTransition-wrapped setIsVisible looks like the most likely site to a reader — a transition that never commits (React 18) would exactly produce "the store state is right, everything reading it via a normal subscription re-renders, but this one local useState inside a startTransition never visibly updates."
Environment
@puckeditor/core:0.23.0- React:
18.3.1 iframe: left at default (enabled)- Zones: legacy
DropZone/zonesshape (not slots) —Data.zones?: Record<string, Content> - Chromium (Playwright-driven, both headless and headed/Xvfb) and manual testing in a normal desktop browser session, same result
Impact
Any overrides.actionBar consumer (and Puck's own default Duplicate/Delete affordance) is effectively invisible on selection in this configuration — the feature works entirely at the state layer but has no on-canvas entry point.
Happy to share a minimal reproduction repo if useful — this was found while integrating overrides.actionBar into a production editor and isolating why a newly-added action never appeared.
Source: puckeditor/puck