docs/solutions/performance-issues/2026-05-01-slate-v2-text-snapshots-should-be-path-stable-for-large-document-typing.md
The large-document typing lane looked like a React rendering problem, but the measured cost was in core snapshot publication. Every text insert paid for a full document snapshot and rollback clone even though text operations do not change paths.
59.63 ms while legacy chunk-on was about 32 ms.editor-update-text-insert at 59.36 ms, core-time:build-change at 40.78 ms, core-time:next-snapshot at 40.13 ms, and core-time:transaction-rollback-children at 12.97 ms.1 ms.Keep the snapshot contract, but make text snapshots path-stable:
previousSnapshot.children in the transaction snapshot and clone it only if rollback actually happens.insert_text, remove_text, and set_selection commits, reuse the previous snapshot index because paths do not change.The core shape:
const getPathStableSnapshot = (
editor: Editor,
previousSnapshot: EditorSnapshot,
operations: readonly Operation[]
): EditorSnapshot | null => {
if (!canBuildPathStableSnapshot(operations)) {
return null
}
let children = previousSnapshot.children as readonly Descendant[]
for (const operation of operations) {
if (operation.type === 'set_selection') continue
if (operation.type !== 'insert_text' && operation.type !== 'remove_text') {
continue
}
children =
updateTextInSnapshotChildren(children, operation) ?? previousSnapshot.children
}
return {
children,
index: previousSnapshot.index,
marks: cloneFrozen(getCurrentMarks(editor)),
selection: cloneFrozen(getCurrentSelection(editor)),
version: getVersion(editor),
} as EditorSnapshot
}
And the collapsed-selection guard:
if (Range.isCollapsed(selection)) {
return uniqPaths(paths)
}
Text operations are path-stable. They change leaf text and cloned ancestors, but they do not change child order or runtime-id path mapping. A full structured clone plus full index rebuild is wasted work for that class.
Rollback still stays correct because the transaction keeps the previous committed snapshot as the rollback source and only clones it when an error path actually restores state. Snapshot readers still receive frozen snapshot objects, and the targeted snapshot contract proves earlier snapshots stay stable across later text commits.
The collapsed-selection cut removes a second O(document) tax from the common caret path. Expanded selections still scan the index because range coverage can legitimately cross many nodes.
beforeinput typing; after this fix direct typing flipped green, while native event overhead remained a separate owner.