Engineering Journal
Schema Editor
Schema Editor

Layer Panels That Don't Mirror the DOM Are Theater

2026-06-04

TLDR

Layer panels with separate data models are wrong by default. They are correct only when every mutation path notifies the panel. That invariant is broken by the first operation that bypasses the notification. Derive the panel from the DOM. It is always correct and requires no maintenance.


What the Industry Does

Figma's layers panel, After Effects' timeline, VS Code's file explorer: they all appear to maintain their own layer/file models with rich UI state. This is the reference pattern for most developers building inspector panels.

The difference: these tools own the objects they display. Figma's layers panel is part of Figma's rendering system. After Effects' timeline is built into the compositor. The layer model and the render model are the same model.

A web-based diagram editor built on top of SVG DOM does not own its render tree. The SVG DOM is the render tree. The panel is a view of something it does not control.

Why It Fails for This Problem Class

Web diagram editors have multiple mutation paths: direct canvas interaction, keyboard shortcuts, undo/redo, import, paste, programmatic operations from other tools. Each path modifies the DOM. The layer panel's sync protocol has to intercept every one.

Undo is the first to break: restoring a snapshot replaces the DOM contents wholesale. One line, one DOM change, potentially hundreds of element mutations. The sync protocol gets one notification (the undo event) and has to rebuild its entire model from scratch anyway. At that point it is not a sync protocol, it is a rebuild on every change.

Import breaks next: SVG imported from another tool adds elements to the DOM without going through the canvas event system. The panel does not know they exist.

Canvas API breaks last: canvasEngine.addElement(el) is called in a dozen places. Each needs to emit a panel event. Missing one means silent desync.

The Better Approach

The DOM walk takes under a millisecond for a diagram with 500 elements. Rebuild the panel on every relevant event. This is not expensive. It is exact.

// After every canvas operation:
function afterOperation() {
    applyOperation();
    rebuildPanel(); // always correct, always fast enough
}

Panel state (expanded groups, scroll position) is preserved by reading it before the rebuild and restoring it after. This is five lines of code. It replaces a synchronization protocol of indefinite complexity.

What You Give Up

A derived panel cannot have speculative state: optimistic updates, animations during drag-reorder, or hover states that survive a rebuild. These require the panel to hold UI state independent of the DOM.

For drag-reorder: animate using CSS transitions on the existing rows, then commit the DOM change and rebuild. The animation happens before the rebuild, so the user sees smooth motion even though the panel rebuilds at the end.

For most interactive editors, this tradeoff is worth it. A panel that is always correct is more valuable than a panel that has smooth animations but occasionally shows the wrong thing.

When the Common Pattern Is Right

Maintain a separate panel model when: you own the underlying data store (not an external DOM), the operation set is closed and finite, and the sync protocol is manageable because there are few mutation paths. Game engines and 3D modeling tools often meet these criteria. Web diagram editors built on SVG DOM almost never do.

Read this post in the full Engineering Journal →