May 12, 2025
Have you ever needed to make a complete copy of a JavaScript object, one where if you change something inside the copy, the original object stays exactly the same? This task, known as "deep cloning" or "deep copying," has historically been tricky in JavaScript, especially when objects contain other objects, arrays, or special types of data nested inside them. Developers often had to rely on less-than-perfect workarounds or external libraries to achieve a truly independent copy.
But there's good news! Did you know there's now a native way built right into JavaScript to handle deep copies reliably?
That's right, a powerful function called structuredClone is now part of the standard JavaScript environment you use. This means you no longer necessarily need complex code or extra tools just to make a full copy of an object.
Here’s a simple example:
JavaScript
// The modern way to copy deeply
In the example above, notice that not only was the main object copied, but also the array nested inside it (attendees) and even the Date object (date).
Everything works exactly as you would expect:
JavaScript
The last check (===) shows that the attendees array in the original object is not the same array in the copied object; it's a completely separate copy.
That's right, structuredClone can do all of the above. Plus, it can:
Clone objects and arrays that are nested inside each other to any level.
Clone objects that have circular references (where properties link back to the original object or earlier parts of the structure).
Clone a wide range of JavaScript types, including Date, Set, Map, Error types, RegExp, ArrayBuffer, Blob, File, ImageData, and many others.
Transfer any transferable objects (like ArrayBuffer).
So, even a complex object like this would be copied correctly:
JavaScript
// Create a circular reference
// All good, fully and deeply copied!
Why Not Just Object Spread (...)?
It’s important to remember that structuredClone performs a deep copy. If you only need a shallow copy (a copy where nested objects or arrays are not copied, but instead the new object just holds references to the same nested items), then the object spread syntax works fine:
JavaScript
//No problem, there are no nested objects or arrays
Other ways to do shallow copies include Object.assign({}, simpleEvent) or Object.create(simpleEvent).
But, if your object has nested items, using shallow copy methods leads to problems:
JavaScript
// Oops - adding "Bob" to the shallow copy also adds him to the original object!
// Oops - changing the date in the shallow copy also changes it in the original object!
As you can see, the shallow copy did not create new copies of the nested date object and attendees array. They are still linked to the original object. If you try to change these nested items through the shallow copy, you will unexpectedly change the original object as well, which can cause major issues.
Why Not JSON.parse(JSON.stringify(x))?
Ah yes, this common technique. It can be quite fast, but it has some important limitations that structuredClone handles better.
Let's use the calendarEvent example again:
JavaScript
// Using JSON methods to copy
If you look at problematicCopy, you would see:
JavaScript
This is not what was intended! The date property should be a Date object, not a string.
This happens because JSON.stringify can only correctly handle basic objects, arrays, and simple data types (like strings, numbers, booleans, null). Other types are handled in ways that might be hard to predict. For example, Date objects are turned into strings. A Set or Map is simply converted into an empty plain object ({}).
JSON.stringify also completely ignores certain values, such as undefined or functions.
If we tried to copy our kitchenSink example using this method:
JavaScript
// Trying to copy with JSON methods
The result would be:
JavaScript
This doesn't give us the correct types!
Also, you would have to remove any circular references before using JSON.stringify, as it throws errors if it finds one.
So, while the JSON.parse(JSON.stringify(x)) method can be useful for simple cases, structuredClone can handle many types and circular references that this method cannot.
Why Not _.cloneDeep from Lodash?
Before structuredClone, Lodash's cloneDeep function was a very common way to solve the deep cloning problem.
And indeed, using cloneDeep from Lodash works as expected:
JavaScript
// This would work correctly:
// clonedEvent.attendees !== calendarEvent.attendees
// clonedEvent.date !== calendarEvent.date
However, there is one drawback here. According to the text, importing just this one function from Lodash can add a notable amount of code to your project (17.4kb minified or 5.3kb gzipped). If you don't import it in the most efficient way, you could accidentally import even more (up to 25kb).
While adding a few kilobytes might not break your project, it's not necessary anymore since browsers already have structuredClone built-in, meaning you don't need to add any extra code for basic deep cloning.
What structuredClone Cannot Clone
There are some types of JavaScript values that structuredClone cannot copy:
Functions: Trying to clone a function will cause an error called DataCloneError.
JavaScript
// Error!
DOM nodes: Elements from a web page (like document.body) also cannot be cloned and will throw a DataCloneError.
JavaScript
// Error!
Property descriptors, setters, and getters: Information about how properties work, like getter and setter functions, is not cloned. Only the resulting value of the property is copied.
JavaScript
// Has a getter function
// Becomes: { foo: 'bar' } - The getter is gone, only the value is copied.
Object prototypes: The chain of objects that determines inheritance is not copied. If you clone an object that was created from a class (instanceof MyClass), the cloned object will have the same properties and values, but it will no longer be recognized as an instance of that specific class.
JavaScript
// Becomes: { foo: 'bar' } - The method is not copied, and the link to the class is lost.
cloned instanceof MyClass // false
Full List of Supported Types
To put it simply, structuredClone can clone anything included in the list below. Anything not on this list cannot be cloned.
JS Built-ins:
*Primitive types included are: number, string, null, undefined, boolean, BigInt.
Error types:
Web/API types:
Browser and Runtime Support
And the good news is, structuredClone is supported in all major web browsers, and also in environments like Node.js and Deno.
The text notes there is a caveat (a minor limitation) with support in Web Workers.
Conclusion
Deeply copying objects is a necessary task sometimes, especially when you need to change a copy without affecting the original. While methods like object spread are fine for shallow copies and JSON.parse(JSON.stringify(x)) can work for simple cases, they have limitations. Using an external library function like Lodash's cloneDeep adds extra code to your project.
The native structuredClone function provides a built-in, powerful, and safe way to perform deep copies of a wide variety of JavaScript types, including complex structures and circular references. Since it's supported in modern environments, it's the recommended modern way to handle deep cloning in JavaScript.
Accelerate Your UI Build Process
Mastering JavaScript's native features like structuredClone is key to writing robust and efficient code. But while you're optimizing your data handling, are you also optimizing how you turn designs into functional user interfaces? Manually translating complex designs from Figma into code can still be a time-consuming step in the development process.
If you're looking for ways to dramatically speed up your front-end development workflow, explore tools designed to bridge the gap between design and code. Superflex helps you go from Figma designs to functional code in seconds, freeing up valuable developer time to focus on logic, performance, and advanced features (like mastering deep cloning!). Discover how you can build user interfaces faster and more efficiently.
Learn more and start at Superflex.ai.