Deep-clone an object
The built-in structuredClone deep-copies nested objects and arrays with no library and no JSON round-trip, plus what it can't clone.
Last updated
function deepClone(value) {
return structuredClone(value);
}
const original = { a: 1, nested: { b: [1, 2, 3] } };
const copy = deepClone(original);
copy.nested.b.push(4);
// original.nested.b is untouched: [1, 2, 3]How it works
structuredClone is a global function (Node 17+, all current browsers) built on the structured-clone algorithm the platform already uses for postMessage and IndexedDB — it walks the whole object graph and copies nested objects and arrays by value, so mutating the copy's nested.b array never touches the original's.
Before this existed, the common trick was JSON.parse(JSON.stringify(value)), which quietly drops or mangles anything JSON can't represent (undefined, functions, Dates become strings). structuredClone is both faster and more honest: it throws on what it can't handle instead of silently corrupting it.
Edge cases to know
- →A function stored as an own property throws DataCloneError. A class instance does NOT throw — its own data properties are copied and the prototype is silently dropped, so you get a plain object back and lose every method and every instanceof check.
- →DOM nodes, and a handful of other platform objects, aren't cloneable either — clone the data you extract from them, not the node itself.
- →It IS deep, unlike Object.assign or the spread operator, but it's a snapshot at call time: later mutations to the original never propagate to the copy, by design.
Part by part
- structuredClone(value)
- A built-in that walks the whole structure, handling cycles, Map, Set, Date, RegExp and typed arrays. It is the algorithm the browser already uses to send data between workers.
- copy.nested.b.push(4)
- The demonstration: mutating deep inside the copy leaves the original untouched, which is the property a shallow copy does not have.
Common mistakes
JSON.parse(JSON.stringify(value))The long-standing workaround, and it silently damages data: Date becomes a string, undefined and functions vanish, Map and Set become empty objects, and a circular reference throws. It works on plain JSON-shaped data and nothing else.
structuredClone(value)Object.assign({}, value)A shallow copy — nested objects are shared, so mutating copy.nested also mutates original.nested. It is often what people mean by clone and almost never what they need.
structuredClone(objectWithMethods)Functions cannot be cloned, so an object carrying methods throws a DataCloneError. Class instances lose their prototype too, arriving as plain objects with the right fields and none of the behaviour.
Questions
What can structuredClone not handle?
Functions and DOM nodes throw. Class instances are cloned as plain objects, losing their prototype, and property descriptors such as getters are not preserved — the current value is copied instead.
Where is it available?
All current browsers and Node 17 and later, as a global needing no import. For older environments a library is still needed, which is much of why the JSON round-trip persisted so long.
Does it handle circular references?
Yes, correctly — the same object appearing twice in the input appears once in the output, with both references pointing at the same clone. That is the case the JSON approach cannot survive at all.
Is it fast enough for large objects?
Generally faster than the JSON round-trip, since it is native and does not build an intermediate string. For very large structures the real question is whether you need a full copy at all, rather than which method to use.