The structuredClone You Should Have Been Using Instead of JSON Hacks
JS Edition
At some point, every JavaScript developer writes this:
const copy = JSON.parse(JSON.stringify(original));
You know it’s ugly. I know it’s ugly. We all wrote it anyway because it worked — until it didn’t.
The thing is, JSON.parse(JSON.stringify()) isn’t a deep clone. It’s a JSON round-trip that happens to look like a deep clone. Those are different things, and the gap between them will eventually bite you in production.
Here’s what silently disappears when you run your object through JSON:
const original = {
name: "Sri",
dob: new Date("1990-01-01"),
greet: () => "hello",
score: undefined,
ref: document.querySelector("body"),
};
const copy = JSON.parse(JSON.stringify(original));
console.log(copy.dob); // "1990-01-01" — a string, not a Date
console.log(copy.greet); // undefined — the function is gone
console.log(copy.score); // undefined — but it's not even in the object
console.log(copy.ref); // {} — a DOM node became an empty objectYour Date became a string. Your functions evaporated. Your undefined values ghosted. Your DOM nodes turned into empty objects. No errors. No warnings. Just silently wrong data.
And if you have circular references anywhere? It throws:
const a = {};
a.self = a;
JSON.parse(JSON.stringify(a)); // TypeError: Converting circular structure to JSONSo the thing we all reached for as the “safe” deep clone is actually pretty unsafe.
Enter structuredClone
Shipped natively in browsers in 2022, structuredClone is the actual deep clone API the platform always should have had:
const copy = structuredClone(original);
That’s it. No import, no library, no npm install. It handles what JSON chokes on:
const original = {
dob: new Date("1990-01-01"),
scores: new Map([["math", 95]]),
flags: new Set([1, 2, 3]),
buffer: new ArrayBuffer(8),
};
const copy = structuredClone(original);
console.log(copy.dob instanceof Date); // true
console.log(copy.scores instanceof Map); // true
console.log(copy.flags instanceof Set); // trueDates stay Dates. Maps stay Maps. Sets stay Sets. Typed arrays, ArrayBuffers, RegExp — all handled correctly. Circular references work too:
const a = {};
a.self = a;
const copy = structuredClone(a);
console.log(copy.self === copy); // trueWhat it still won’t clone
structuredClone uses the Structured Clone Algorithm, which is powerful but not unlimited. A few things it explicitly refuses:
// Functions — will throw
structuredClone({ fn: () => {} }); // DataCloneError
// DOM nodes — will throw
structuredClone({ el: document.body }); // DataCloneError
// Class instances lose their prototype chain
class User {
greet() { return "hi"; }
}
const u = new User();
const copy = structuredClone(u);
copy.greet(); // TypeError: copy.greet is not a functionFunctions are not cloneable by design — they’re not data, they’re behaviour. DOM nodes can’t be structurally cloned because they carry too much browser state. And class instances get cloned as plain objects, so the prototype chain is gone.
If your data is plain objects with nested arrays, dates, maps, and sets — structuredClone is perfect. If you’re cloning class instances with methods, you need a different approach.
The transferable trick
There’s one more thing structuredClone can do that nothing else can — transfer ownership of data instead of copying it:
const buffer = new ArrayBuffer(1024);
const copy = structuredClone(buffer, { transfer: [buffer] });
// buffer is now detached — zero-copy move, not a clone
console.log(buffer.byteLength); // 0This is primarily useful when moving large binary data to a Web Worker. Instead of copying megabytes of data across the thread boundary, you transfer ownership of it. The original becomes unusable, and the worker gets the memory directly. If you’re doing anything with image processing, audio, or large datasets in workers, this matters.
Should you replace every JSON.parse(JSON.stringify()) today?
Probably not a big refactor. But new code? Yes, default to structuredClone. It’s supported in every major browser since 2022 and in Node since v17. The only reason to reach for JSON round-tripping now is if you specifically need serializable output — like sending data over the wire or saving to localStorage. In that case JSON is the right tool. For everything else, structuredClone is what you actually want.
The JSON trick was never a deep clone. It was a workaround that happened to work often enough that we stopped questioning it.


