docs/solutions/logic-errors/2026-05-06-slate-react-foreign-dom-selections-must-be-ignored-before-import.md
DOM selection crash issues like #4789 and #4984 are not solved by blindly
importing every native Selection. Some native ranges are partly foreign to the
current editor: they can start outside Slate, or cross from a parent editor into
a nested editor.
Cannot resolve a Slate point from DOM point appears after an outside-to-inside
browser selection.Lock the behavior with native DOM selection rows, not only semantic Slate selection helpers:
await editor.root.evaluate((element: HTMLElement) => {
const outside = element.ownerDocument.createElement('p')
outside.textContent = 'outside selection source'
element.parentElement?.insertBefore(outside, element)
const editorText = element.querySelector('[data-slate-string]')?.firstChild
const outsideText = outside.firstChild
if (!editorText || !outsideText) {
throw new Error('Cannot create outside-to-editor selection')
}
const range = element.ownerDocument.createRange()
range.setStart(outsideText, 0)
range.setEnd(editorText, 4)
const selection = element.ownerDocument.getSelection()
selection?.removeAllRanges()
selection?.addRange(range)
element.ownerDocument.dispatchEvent(
new Event('selectionchange', { bubbles: true })
)
})
runtimeErrors.assertNone()
await editor.click()
await page.keyboard.type('Z')
await expect.poll(() => editor.get.modelText()).toContain('Z')
For nested editors, create the actual cross-editor native range:
const range = outerElement.ownerDocument.createRange()
range.setStart(outerText, 0)
range.setEnd(nestedText, 4)
const selection = outerElement.ownerDocument.getSelection()
selection?.removeAllRanges()
selection?.addRange(range)
outerElement.ownerDocument.dispatchEvent(
new Event('selectionchange', { bubbles: true })
)
Then assert no runtime errors and prove follow-up input stays scoped to the focused editor.
The runtime selection bridge should import only selections whose endpoints belong to the current editor's editable surface. Foreign anchors and nested editor endpoints are boundary conditions; importing them corrupts ownership.
The browser rows prove the real failure mode: a native Selection object with
mixed ownership exists in the document, selectionchange fires, Slate does not
throw, and normal editor input still works after focus returns.
Range shape that the issue
describes. Do not replace it with editor.selection.select.recordSlateBrowserRuntimeErrors(page).Fixes lines until their exact repro rows pass.