packages/lexical-website/docs/concepts/node-cloning.md
Understanding how to properly clone and modify nodes is crucial for working with Lexical. This guide explains the different cloning mechanisms and when to use each one.
Lexical provides several ways to clone nodes, each serving a different purpose:
static clone() - Internal API for state management$copyNode() - Public API for creating new nodesgetWritable() - Public API for node modificationsclone Methodclone?The clone method is a static method required by all Lexical nodes that creates the next version of a node. However, it's important to understand that this is an internal API used by Lexical's state management system.
class MyCustomNode extends ElementNode {
static clone(node: MyCustomNode): MyCustomNode {
// ✅ Correct implementation
return new MyCustomNode(node.__someData, node.__key);
}
}
clone Used?Internal State Management
getWritable()Node Mutations
// Example of a node method implementation
class MyCustomNode extends ElementNode {
setData(data: string): this {
const self = this.getWritable();
self.__data = data;
return self;
}
}
node.setSomeData("new data");
clone?// ❌ Never do this
function $duplicateNode(node: MyCustomNode) {
return MyCustomNode.clone(node); // Don't call clone directly
}
$copyNode$copyNode?$copyNode is the public API for creating a copy of a node with a new key. Use this when you need to create a duplicate node.
By default, all properties and NodeState will be copied to the new node, and then resetOnCopyNodeFrom will be called to
allow the node to optionally reset certain properties (and NodeState configured with resetOnCopyNode: true) to defaults
(such as the checked state of a ListItemNode).
// ✅ Correct: Using $copyNode
const copy = $copyNode(existingNode);
$copyNode?// ✅ Correct: Duplicating a node
const duplicate = $copyNode(originalNode);
someParent.append(duplicate);
getWritablegetWritable?getWritable is an internal API used within node method implementations to get a mutable version of a node. Node consumers should use the node's public methods instead.
// ✅ Correct: Implementation of a node method
class MyCustomNode extends ElementNode {
setData(data: string): this {
const self = this.getWritable();
self.__data = data;
return self;
}
}
// ✅ Correct: Using the node
const node = new MyCustomNode();
node.setData("new value");
// ✅ Correct: Modifying a node
function $updateNodeData(node: MyCustomNode, newData: string): MyCustomNode {
return node.setData(newData);
}
// ❌ Incorrect: Don't clone manually
function $updateNodeDataWrong(node: MyCustomNode, newData: string): MyCustomNode {
const clone = MyCustomNode.clone(node);
clone.setData(newData);
return clone;
}
// ✅ Correct: Copying a node
function $duplicateNode(node: MyCustomNode): MyCustomNode {
return $copyNode(node);
}
// ❌ Incorrect: Don't use clone
function $duplicateNodeWrong(node: MyCustomNode): MyCustomNode {
return node.constructor.clone(node);
}
Referential Integrity
getWritable() ensures proper EditorState updates with new clonesState Management
class MyCustomNode extends ElementNode {
setData(data: string): this {
const self = this.getWritable();
self.__data = data; // Properly tracked by editor
return self;
}
}
test('node modification', async () => {
await editor.update(() => {
const node = new MyCustomNode("test");
node.setData("new data");
// ✅ Correct: Use $copyNode for duplication
const copy = $copyNode(node);
});
});
Q: How do I duplicate a node?
A: Use $copyNode(node) to create a new copy with a new key.
Q: When should I use clone?
A: Never directly. Use $copyNode() or getWritable() instead.