docs/solutions/ui-bugs/2026-03-27-version-history-demo-must-clone-snapshots-per-editor.md
The version history demo mounted the same Slate node objects in three places at once: the live editor, the saved revision view, and the diff comparison flow. That breaks Slate's DOM-to-node bookkeeping and also means saved revisions are not real snapshots.
Unable to find the path for Slate node errors for the trailing text node.Clone every value that crosses an editor or revision boundary, and keep the diff-only plugin out of the editable editor.
export const createVersionSnapshot = (value: Value): Value => cloneDeep(value);
const basePlugins = [
...BasicMarksKit,
InlinePlugin.withComponent(InlineElement),
InlineVoidPlugin.withComponent(InlineVoidElement),
];
const diffPlugins = [...basePlugins, DiffPlugin];
Use those snapshots for initial state, change handling, saved revisions, and diff inputs:
const [revisions, setRevisions] = React.useState<Value[]>(() => [
createVersionSnapshot(initialValue),
]);
const [value, setValue] = React.useState<Value>(() =>
createVersionSnapshot(initialValue)
);
const saveRevision = () => {
setRevisions([...revisions, createVersionSnapshot(value)]);
};
<VersionHistoryPlate
onChange={({ value }) => setValue(createVersionSnapshot(value))}
editor={editor}
/>;
Clone both sides before computing the diff:
return computeDiff(
createVersionSnapshot(previous),
createVersionSnapshot(current),
{
isInline: editor.api.isInline,
lineBreakChar: '¶',
}
);
Slate expects each mounted editor tree to own its own node graph. When two editors share the same node objects, Slate can resolve a DOM point against the wrong tree or fail to find a path entirely. Deep-cloned snapshots give each editor its own stable node identities, and saved revisions stop mutating alongside the live draft. Splitting basePlugins from diffPlugins also keeps diff rendering behavior scoped to the comparison pane instead of the editable surface.
Value object in multiple editors. If a value becomes a snapshot, clone it first.#4875