JSON.parse(JSON.stringify()) is the most familiar deep-copy trick in JavaScript, but it silently corrupts your data: Dates turn into strings, Maps and Sets become empty objects, and an object with a circular reference throws right away. structuredClone() — a standard API built into modern browsers and Node.js 17+ — is a true deep copy: it keeps Date, Map, Set, and ArrayBuffer intact, and handles circular references as well.

The Problem

Say you need to duplicate a config object before editing it, so you can roll back when the user clicks Cancel. Many developers stringify the object to JSON and parse it back — fast, no library needed — but the price is in the data types JSON does not understand:

const user = {
name: "Linh",
createdAt: new Date("2026-11-15T09:00:00"),
tags: new Set(["dev", "blog"]),
};
const copy = JSON.parse(JSON.stringify(user));
console.log(typeof copy.createdAt); // "string" — Date type lost
console.log(copy.tags); // {} — Set is gone
copy.createdAt.getFullYear(); // TypeError: getFullYear is not a function

And that is not all: a single circular reference — common in folder trees or state with parent–child relationships — makes JSON.stringify throw immediately:

const node = { value: 1 };
node.next = node;
JSON.stringify(node); // TypeError: Converting circular structure to JSON

The result: data types lost, and no copy at all.

The Solution

structuredClone() was built for exactly this job. It is a global function, no import needed, and returns a fully independent deep copy:

const user = {
name: "Linh",
createdAt: new Date("2026-11-15T09:00:00"),
tags: new Set(["dev", "blog"]),
};
user.self = user; // circular reference — still clones fine
const copy = structuredClone(user);
console.log(copy.createdAt instanceof Date); // true
console.log(copy.tags.has("dev")); // true
console.log(copy.self === copy); // true — the cycle is preserved
copy.name = "Changed";
console.log(user.name); // "Linh" — the original is untouched

Three things worth noting: data types are preserved — a Date stays a Date, a Set stays a Set. Circular references keep their structure: copy.self points to the copy itself, not to the original. And the copy is fully independent: editing it never affects the source object. The function also accepts a { transfer } option to move ownership of an ArrayBuffer instead of copying it — handy when sending large data to a worker.

Apply It to Your Project

Use structuredClone whenever you need a quick “snapshot” of state before changing it: a form being edited, filters being picked, settings before applying a preset. A familiar pattern: save a copy when the edit form opens, overwrite the original data only when the user clicks Save — click Cancel and the copy is simply discarded. Keep two limits in mind: the function cannot copy functions, DOM nodes, or WeakMaps (it throws DataCloneError), and objects created from a class lose their prototype — the copy keeps only the data, not the class methods.

Related Posts

Frequently Asked Questions

Where can I use structuredClone?

Node.js 17+ and all modern browsers (Chrome 98+, Firefox 94+, Safari 15.4+). On older environments, use lodash cloneDeep, or accept JSON when the data only contains simple types.

Can structuredClone copy functions?

No. Functions, DOM nodes, WeakMaps, and WeakSets throw a DataCloneError. The function is only for plain data — objects, arrays, Date, Map, Set, and ArrayBuffer.

What happens to objects created from a class?

The data is preserved, but the prototype is lost: the copy becomes a plain object with no class methods. To keep the methods, write your own clone method in the class, or serialize and restore through the constructor.

Is structuredClone faster than JSON.parse(JSON.stringify())?

For small, simple data, the JSON trick is usually a bit faster. But with data that contains Date, Map, Set, or circular references, structuredClone is both correct and error-free — so correctness should win.

When should I still use JSON.parse(JSON.stringify())?

When the data only contains strings, numbers, booleans, arrays, and plain objects, and you need compatibility with very old environments. It is also useful when you deliberately want to “clean” an object — dropping undefined values and functions before saving.

💬 Have you tried this in your own project? If you hit an error or know a shorter way, leave a comment below — I read them all and will add the best ideas to the post.

References