Copying objects in JavaScript is tricky. A simple assignment doesn’t copy anything. It just creates another reference to the same object. Change one, and you change both.
For years, developers used workarounds like JSON.parse(JSON.stringify(...)) or lodash’s cloneDeep. These work, but they have real limits.
Then JavaScript added structuredClone. It’s built into the browser and Node.js. It deep copies objects cleanly, with no third-party libraries needed.
This article shows you what it is, how it works, and when to use it.
The Problem: Copying Objects in JavaScript
First, let’s understand why copying is hard.
Shallow Copy Problem
const original = { name: "Alice", address: { city: "London" } };
const copy = { ...original };
copy.address.city = "Paris";
console.log(original.address.city); // "Paris" - original changed too!
The spread operator only copies the top level. The nested address object is still shared. This is a shallow copy.
The JSON Trick and Its Limits
Many developers use this pattern:
const copy = JSON.parse(JSON.stringify(original));
It creates a true deep copy. But it breaks with certain data types.
const data = {
date: new Date(),
fn: () => "hello",
undef: undefined,
map: new Map([["key", "value"]])
};
const copy = JSON.parse(JSON.stringify(data));
console.log(copy.date); // A string, not a Date object
console.log(copy.fn); // undefined, function is lost
console.log(copy.undef); // key is missing entirely
console.log(copy.map); // {}, Map is lost
That’s a lot of data loss. This is where structuredClone comes in.
What is structuredClone?
structuredClone is a global function built into JavaScript. It creates a deep copy of an object using the Structured Clone Algorithm, a web standard used to copy data between workers and tabs.
You don’t need to install anything. It’s available in:
- Chrome 98+
- Firefox 94+
- Node.js 17+
- Safari 15.4+
Basic Usage
const original = {
name: "Alice",
address: { city: "London" }
};
const copy = structuredClone(original);
copy.address.city = "Paris";
console.log(original.address.city); // "London" - unchanged
console.log(copy.address.city); // "Paris"
The nested object is fully copied. Changing one does not affect the other.
What structuredClone Handles That JSON Can’t
structuredClone supports many data types that trip up the JSON approach.
Dates
const original = { created: new Date("2024-01-01") };
const copy = structuredClone(original);
console.log(copy.created instanceof Date); // true
console.log(copy.created.getFullYear()); // 2024
The Date stays a Date, not a string.
Maps and Sets
const original = {
tags: new Set(["js", "web"]),
scores: new Map([["alice", 95]])
};
const copy = structuredClone(original);
copy.tags.add("node");
copy.scores.set("alice", 100);
console.log(original.tags.has("node")); // false
console.log(original.scores.get("alice")); // 95
Both Map and Set are copied properly.
Arrays with Objects
const original = [{ id: 1 }, { id: 2 }];
const copy = structuredClone(original);
copy[0].id = 99;
console.log(original[0].id); // 1 - untouched
RegExp
const original = { pattern: /hello/gi };
const copy = structuredClone(original);
console.log(copy.pattern instanceof RegExp); // true
console.log(copy.pattern.flags); // "gi"
ArrayBuffer and Typed Arrays
const buffer = new ArrayBuffer(8);
const copy = structuredClone(buffer);
console.log(copy.byteLength); // 8
What structuredClone Cannot Copy
It’s powerful, but not magic. Some things will throw an error.
Functions
const obj = { greet: () => "hello" };
structuredClone(obj); // DataCloneError: Functions cannot be cloned
DOM Nodes
const obj = { el: document.body };
structuredClone(obj); // DataCloneError
Class Instances (Loses Prototype)
class User {
constructor(name) { this.name = name; }
greet() { return `Hi, I'm ${this.name}`; }
}
const user = new User("Alice");
const copy = structuredClone(user);
console.log(copy.name); // "Alice" - data is copied
console.log(copy.greet()); // TypeError: copy.greet is not a function
The data copies over, but the class methods don’t. The copy is a plain object.
Transferable Objects (Bonus Feature)
structuredClone has a second argument for transferable objects like ArrayBuffer. Transferring moves the data instead of copying it. The original becomes empty. This is useful for performance when sharing data with Web Workers.
const buffer = new ArrayBuffer(1024);
const copy = structuredClone(buffer, { transfer: [buffer] });
console.log(buffer.byteLength); // 0 - ownership transferred
console.log(copy.byteLength); // 1024
Only use this when you no longer need the original.
structuredClone vs Other Methods
| Method | Deep Copy | Handles Dates | Handles Maps/Sets | Handles Functions |
|---|---|---|---|---|
= (assignment) |
No | N/A | N/A | N/A |
{ ...obj } (spread) |
Shallow only | Yes | Yes | Yes |
JSON.parse/stringify |
Yes | No (becomes string) | No (lost) | No (lost) |
structuredClone |
Yes | Yes | Yes | No (throws error) |
lodash cloneDeep |
Yes | Yes | Yes | Yes (sort of) |
If you need to copy functions or class instances with methods, lodash cloneDeep is still worth considering. For everything else, structuredClone is the cleaner built-in choice.
Real World Use Cases
State management: Copy state before updating it, so you can track what changed.
const prevState = structuredClone(currentState);
currentState.user.name = "Bob";
Form data reset: Save the original form values, let the user edit, then restore on cancel.
const saved = structuredClone(formValues);
function resetForm() {
formValues = structuredClone(saved);
}
Avoiding side effects in functions: Pass a copy into a function so it can’t mutate your data.
function processOrder(order) {
const copy = structuredClone(order);
copy.status = "processing";
return copy;
}
Undo history: Store snapshots of data at each step.
const history = [];
function save(state) {
history.push(structuredClone(state));
}
FAQs
Q: Is structuredClone available in all browsers?
It works in all modern browsers (Chrome 98+, Firefox 94+, Safari 15.4+). For older browser support, use a polyfill or fall back to lodash cloneDeep.
Q: Is structuredClone available in Node.js?
Yes, from Node.js 17 onwards. For older versions, use v8.deserialize(v8.serialize(obj)) or lodash.
Q: What happens if I try to clone a function?
It throws a DataCloneError. Functions cannot be cloned. Remove them before cloning or handle them separately.
Q: Can I use structuredClone on arrays?
Yes. It deep copies arrays, including nested objects inside them.
const arr = [{ x: 1 }, { x: 2 }];
const copy = structuredClone(arr);
copy[0].x = 99;
console.log(arr[0].x); // 1
Q: Does structuredClone copy class methods?
No. It copies the data (own properties) but drops the prototype. The result is a plain object.
Q: Is structuredClone faster than JSON.parse/stringify?
In most cases, yes. It skips the string serialization step. For very large objects, the difference can be significant.
Q: Can I clone a Map or Set with structuredClone?
Yes. This is one of its biggest advantages over the JSON approach. Maps and Sets are cloned correctly.
Q: What is the Structured Clone Algorithm?
It’s a web standard that defines how browsers copy complex data, like when passing messages between tabs or Web Workers. structuredClone exposes this same algorithm as a simple function.
Summary
structuredClone is the best built-in way to deep copy objects in JavaScript. It handles Date, Map, Set, RegExp, and typed arrays without any extra setup or libraries.
It won’t copy functions or class methods, so keep that in mind. But for plain data, config objects, state, and form values, it’s the cleanest tool available.
Stop using the JSON trick. Use structuredClone instead.
TypeScript 6 Beta Released: What You Need to Know