Why structuredClone() changes deep copying
Deep-copying a JavaScript value used to mean pulling in a third-party library or relying on a clever but fragile JSON trick. The platform now ships structuredClone(), a native function that performs true deep copies using the same algorithm the browser has long used internally for serialization.

Browser support: Chrome 98, Edge 98, Firefox 94, Safari 15.4.
What shallow copying actually gives you
Most copy operations in JavaScript are shallow. The object spread operator ... is the usual way to make one:
const myOriginal = {
someProp: "with a string value",
anotherProp: {
withAnotherProp: 1,
andAnotherProp: true
}
};
const myShallowCopy = {...myOriginal};
The new object gets its own copy of the top-level properties. Mutating a property directly on the copy leaves the original untouched:
myShallowCopy.aNewProp = "a new value";
console.log(myOriginal.aNewProp)
// ^ logs `undefined`
The problem appears with nested objects. Spreading copies the reference to a nested object, not the object itself. A change to a nested property propagates to both the copy and the original:
myShallowCopy.anotherProp.aNewProp = "a new value";
console.log(myOriginal.anotherProp.aNewProp)
// ^ logs `a new value`
That is because JavaScript stores non-primitive values by reference. Copying the reference is cheap, but it means two pieces of code can silently share and mutate the same underlying object.
The old deep-copy workarounds
The most widespread workaround was a JSON round-trip:
const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));
Browsers optimized this pattern heavily, and it is fast for many cases. But it has three notable failure modes:
- Recursive structures —
JSON.stringify()throws when given a linked list, tree, or any object with circular references. - Non-plain built-ins —
Map,Set,Date,RegExp, andArrayBuffervalues cause it to throw outright. - Functions — these are silently dropped; the resulting copy is missing data you may not realize you lost.
Structured cloning arrives
Browsers already had a robust deep-cloning algorithm. Storing a value in IndexedDB and passing a value to a Web Worker via postMessage() both rely on it. The catch was that the algorithm was not directly exposed to page JavaScript. That changed with structuredClone(), which runs the same logic in user code:
const myDeepCopy = structuredClone(myOriginal);
A single call replaces the JSON hack and covers most of what the old workaround could not handle: circular data structures and many built-in types are supported, and the results are generally more robust and often faster.
What structuredClone still cannot do
The new API does not fix everything. Three limitations are worth keeping in mind:
- Prototypes: A class instance passed to
structuredClone()becomes a plain object; the prototype chain is discarded. - Functions: Including one in your data causes
structuredClone()to throw aDataCloneError. - Non-cloneable values:
Errorobjects and DOM nodes cannot be cloned and also trigger a throw.
For applications that genuinely need those behaviors, Lodash’s cloneDeep() or another custom cloning library is still the fallback.
Making it the default
For larger objects, structured cloning has historically beaten JSON.parse() by a significant margin, though JSON.parse() can still win on very small payloads. Since structuredClone() arrives without the abuse of JSON APIs or the overhead of transferring messages through postMessage(), it is the sensible first choice for any new deep-copy requirement.



