docs/solutions/ui-bugs/2026-04-22-slate-react-keydown-must-import-dom-selection-before-model-owned-navigation.md
Browser-native vertical movement and Slate/model-owned horizontal movement can fight if Slate does not import the current DOM selection before handling the next keydown.
ArrowDown moved the visible DOM caret from paragraph 1 to paragraph 2.ArrowRight then used stale model selection and snapped the DOM caret back to
paragraph 1.onDOMSelectionChange handler. The actual
selectionchange was queued through the debounced scheduler.selectRange as browser setup. It is useful, but it is not raw DOM
selection proof unless the helper also places the DOM selection.Add a central DOM-to-model import before model-owned key handling:
if (!isInteractiveInternalTarget(editor, event.target)) {
syncEditorSelectionFromDOM({
editor,
preferModelSelectionForInputRef,
})
}
The helper reads the current DOM selection and selects the equivalent Slate range unless the editor is explicitly in model-owned mode:
if (range && (!selection || !Range.equals(selection, range))) {
Transforms.select(editor, range)
}
Also fix browser helpers that were model-only. If a test claims browser navigation, setup must place DOM selection too, not just call the Slate browser handle.
The browser is allowed to own vertical caret movement. Slate is allowed to own some horizontal or structural navigation. The boundary between those two modes must be explicit.
Before a model-owned key reads Editor.getLiveSelection(editor), Slate must
import the current in-editor DOM selection unless a model-owned operation is
already the active source of truth.
This mirrors the discipline used by better editor architectures:
ArrowDown followed by ArrowRight, not only ArrowLeft/ArrowRight in a
single line.