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.
1 const data = {2 date: new Date(),3 regex: /hello/gi,4 map: new Map([["key", "value"]]),5 };67 const jsonCopy = JSON.parse(JSON.stringify(data));8 console.log(jsonCopy);9 // Output: { date: "2025-02-02T12:00:00.000Z", regex: {}, map: {} }
You lose critical types:
Date
becomes a stringRegExp
becomes an empty objectMap
becomes
Worse, if your object has circular references, it’ll crash:
1 const node = {};2 node.self = node;34 JSON.stringify(node); // ❌ TypeError: Converting circular structure to JSON
Enter structuredClone()
:
1 const structuredCopy = structuredClone(data);23 console.log(structuredCopy.date instanceof Date); // true4 console.log(structuredCopy.regex instanceof RegExp); // true5 console.log(structuredCopy.map instanceof Map); // true
It 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:
1 const buffer = new ArrayBuffer(16);2 const clone = structuredClone(buffer);34 console.log(clone.byteLength); // 165 console.log(clone === buffer); // false
Useful for transferring binary data to Web Workers or BroadcastChannel.
🔁 Deep Nesting and Circular Structures
structuredClone()
can also handle circular references:
1 const node = { value: 1 };2 node.self = node;34 const clone = structuredClone(node);5 console.log(clone.self === clone); // true
This 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
1 const withFn = {2 sayHi: () => console.log("hi"),3 };45 structuredClone(withFn); // ❌ DataCloneError
Functions can’t be cloned. Behavior (code) is lost.
❌ 2. DOM Elements
1 const div = document.createElement("div");2 structuredClone(div); // ❌ DataCloneError
UI elements aren't cloneable — only data is.
❌ 3. Prototypes and Class Instances
1 class MyClass {2 constructor(x) {3 this.x = x;4 }5 }67 const instance = new MyClass(42);8 const clone = structuredClone(instance);910 console.log(clone instanceof MyClass); // false11 console.log(clone.x); // 42
Only 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
1 import cloneDeep from "lodash/cloneDeep";23 const obj = { a: 1, b: { c: 2 } };4 const deep = cloneDeep(obj);
Works for nested objects and arrays — but:
- Can’t clone
Map
,Set
,Date
accurately - 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-clone
polyfill- 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.
1 function saveSnapshot(state) {2 historyStack.push(structuredClone(state));3 }
- Preserves deep state
- Avoids accidental mutations
- Eliminates bug-prone manual deep clones
2. State Sync Between Tabs with BroadcastChannel
1 const channel = new BroadcastChannel("sync");2 channel.postMessage(structuredClone(store));
On receiving:
1 channel.onmessage = (e) => {2 syncWithStore(e.data);3 };
Ensures:
Map
,Date
,Blob
stay intact- Cycles don’t crash the sync
- Fast, native serialization
3. Passing Data to Web Workers
1 const worker = new Worker("worker.js");2 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.