Mastering Deep Object Cloning in JavaScript with structuredClone()
Deep cloning in JavaScript has always been tricky. Developers often relied on hacks like JSON.stringify() + JSON.parse() or external libraries, which only worked for the simplest cases. But now, there’s a native and powerful alternative: structuredClone().
In this guide, you’ll learn how structuredClone() works, what it supports, where it breaks, how it compares to other methods, and where to use it in real-world apps.

🔄 Why structuredClone() Is Not Just JSON 2.0
Let’s start with a classic problem.
const data = {
date: new Date(),
regex: /hello/gi,
map: new Map([['key', 'value']]),
};
const jsonCopy = JSON.parse(JSON.stringify(data));
console.log(jsonCopy);
// Output: { date: "2025-02-02T12:00:00.000Z", regex: {}, map: {} }You lose critical types:
-
Datebecomes a string -
RegExpbecomes an empty object -
Mapbecomes
Worse, if your object has circular references, it’ll crash:
const node = {};
node.self = node;
JSON.stringify(node); // ❌ TypeError: Converting circular structure to JSONEnter structuredClone():
const structuredCopy = structuredClone(data);
console.log(structuredCopy.date instanceof Date); // true
console.log(structuredCopy.regex instanceof RegExp); // true
console.log(structuredCopy.map instanceof Map); // trueIt handles these cases correctly, deeply, and safely.
✅ What structuredClone() Supports
This method works with a wide range of types:
✅ Primitives:
undefined, null, boolean, number, bigint, string, symbol
✅ Data Structures:
- Objects and arrays
-
Date,RegExp,Map,Set - Typed arrays,
ArrayBuffer,SharedArrayBuffer -
Blob,File,ImageData,MessagePort,FileList - Browser-specific objects like
MessageChannel,OffscreenCanvas
🔬 Binary Example:
const buffer = new ArrayBuffer(16);
const clone = structuredClone(buffer);
console.log(clone.byteLength); // 16
console.log(clone === buffer); // falseUseful for transferring binary data to Web Workers or BroadcastChannel.
🔁 Deep Nesting and Circular Structures
structuredClone() can also handle circular references:
const node = { value: 1 };
node.self = node;
const clone = structuredClone(node);
console.log(clone.self === clone); // trueThis makes it perfect for serializing graphs, trees, and cyclic structures, where JSON completely fails.
🧱 Limitations of structuredClone()
While powerful, it doesn’t handle everything.
❌ 1. Functions
const withFn = {
sayHi: () => console.log('hi'),
};
structuredClone(withFn); // ❌ DataCloneErrorFunctions can’t be cloned. Behavior (code) is lost.
❌ 2. DOM Elements
const div = document.createElement('div');
structuredClone(div); // ❌ DataCloneErrorUI elements aren’t cloneable — only data is.
❌ 3. Prototypes and Class Instances
class MyClass {
constructor(x) {
this.x = x;
}
}
const instance = new MyClass(42);
const clone = structuredClone(instance);
console.log(clone instanceof MyClass); // false
console.log(clone.x); // 42Only the plain object structure is cloned — not the prototype chain.
⚖️ Performance: Is It Slow?
Compared to JSON.parse(JSON.stringify(...)), yes — structuredClone() is slower for flat structures.
But it’s much more stable, and actually faster for:
- Nested or deeply recursive objects
- Objects with circular references
- Binary data or typed arrays
If performance is critical (e.g., cloning every animation frame), consider alternatives like Object.assign() (shallow) or lodash/cloneDeep.
🆚 Alternatives and Polyfills
📦 Lodash cloneDeep
import cloneDeep from 'lodash/cloneDeep';
import cloneDeep from 'lodash/cloneDeep';
const obj = { a: 1, b: { c: 2 } };
const deep = cloneDeep(obj);Works for nested objects and arrays — but:
- Can’t clone
Map,Set,Dateaccurately - Won’t handle circular structures unless configured
🌐 Browser Support
- ✅ Supported: Chrome 98+, Firefox 94+, Safari 15+, Node.js 17+
- ❌ Not Supported: IE11, Safari < 14, Node < 17
For older environments, use:
-
structured-clonepolyfill - Fallback to
cloneDeep()orJSON.parse()with checks
🚀 Real-World Use Cases
1. Undo/Redo in Rich Editors
When building complex editors (think Notion, Figma, Miro), you need to clone the entire state, including nested structures, maps, and cyclic references.
function saveSnapshot(state) {
historyStack.push(structuredClone(state));
}- Preserves deep state
- Avoids accidental mutations
- Eliminates bug-prone manual deep clones
2. State Sync Between Tabs with BroadcastChannel
const channel = new BroadcastChannel('sync');
channel.postMessage(structuredClone(store));On receiving:
channel.onmessage = e => {
syncWithStore(e.data);
};Ensures:
-
Map,Date,Blobstay intact - Cycles don’t crash the sync
- Fast, native serialization
3. Passing Data to Web Workers
const worker = new Worker('worker.js');
worker.postMessage(structuredClone(bigData));Structured clone avoids Transferable limitations and keeps data safe.
⚠️ When Not to Use It
| ❌ Scenario | Alternative |
|---|---|
| You need methods or class instances | Manual clone or class-transformer |
| Performance-critical, cloning every frame | Shallow copy or tuned lib |
| Old browser/Node.js version | Polyfill or fallback |
| Cloning UI elements or DOM nodes | Manual serialization |
🧠 Final Thoughts
structuredClone() is the modern way to deep clone in JavaScript:
- Safe
- Native
- Handles complex structures and cycles
- Ideal for serialization, syncing, undo stacks
It’s especially powerful in React, Vue, and Svelte apps for state snapshotting, and is perfect for passing data to IndexedDB, postMessage, or between browser tabs.
Use it with care — know where it shines and where it doesn’t. And say goodbye to JSON.parse(JSON.stringify(...)) once and for all.