EditorState is the immutable snapshot of the editor’s content and selection at a point in time.
The EditorState is an immutable snapshot representing the complete state of a Lexical editor at a specific moment. It contains the node tree (the document structure) and the current selection.
class EditorState { /** Map of all nodes by their keys */ _nodeMap: NodeMap; // Map<NodeKey, LexicalNode> /** Current selection (or null) */ _selection: null | BaseSelection; /** Whether this state is read-only */ _readOnly: boolean; /** Whether this state requires synchronous flush */ _flushSync: boolean;}
Editor states are deeply frozen after reconciliation:
editor.update(() => { const root = $getRoot(); const paragraph = $createParagraphNode(); root.append(paragraph); // After reconciliation, the state becomes read-only});// Outside update, trying to mutate throws an errorconst state = editor.getEditorState();state._nodeMap.set('key', node); // ❌ Throws in dev mode
When you call mutating methods on nodes during an update, Lexical automatically clones them:
editor.update(() => { const paragraph = $getRoot().getFirstChild(); // This actually creates a mutable clone and updates it paragraph.append($createTextNode('New text')); // The original paragraph node from the previous state is unchanged});
This is handled transparently by the getWritable() method that all mutation methods call internally.
const currentState = editor.getEditorState();const clonedState = currentState.clone();// With a new selectionconst clonedWithSelection = currentState.clone(newSelection);
// Parse from JSONconst state = editor.parseEditorState(jsonString);// Set it on the editoreditor.setEditorState(state, { tag: 'history-merge' // Optional tag});
setEditorState() triggers a full reconciliation and will run transforms. For simple state restoration, this is the right approach.
// As a JSON objectconst state = editor.parseEditorState(jsonObject);// As a stringconst state = editor.parseEditorState(jsonString);// With an update functionconst state = editor.parseEditorState(jsonString, () => { // Optionally modify state during parse const root = $getRoot(); // ... modifications});editor.setEditorState(state);
const state = editor.getEditorState();if (state.isEmpty()) { // State only contains the root node and has no selection console.log('Editor is empty');}
An empty state has exactly one node (the root) and no selection.