docs/solutions/performance-issues/2026-05-08-dom-selection-bridges-must-stay-cheap-on-selectionchange.md
DOM selection import needs explicit ownership classification, but
selectionchange is a hot browser path. A clean bridge can become slow if it
allocates structured reason objects, scans arbitrary DOM, or exposes classifier
details through public React APIs.
selectionchange, but classification
still runs on a frequent browser event.Keep the bridge private and make the hot path primitive:
Range | null;selectionchange handling;Range | null state only.The execution plan records the specific source owners:
.tmp/slate-v2/packages/slate-react/src/editable/runtime-selection-engine.ts
owns native selectionchange throttling..tmp/slate-v2/packages/slate-react/src/editable/selection-runtime.ts and
.tmp/slate-v2/packages/slate-react/src/hooks/use-editor-selector.tsx own
selection fanout filters..tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts owns
fast DOM selection range creation, fail-closed import, model export, and
scroll timing..tmp/slate-v2/packages/slate-react/src/editable/runtime-root-engine.ts owns
React listener/effect wiring.selectionchange can fire often and can be triggered by both user action and
runtime repair. The editor should decide ownership before importing DOM state,
but that decision should not allocate a fresh object graph or traverse broad DOM
on every event.
Keeping bridge reasons private also preserves Slate's API shape. Public
consumers care whether the model selection is a Range or null; they should
not need to understand stale DOM, nested editor, shadow-root, or app-owned
selection classifications.
Fixes #... claim.