The structuredClone function creates deep copies of values using the structured clone algorithm. Here's how to use it.
Basic Usage#
1// Deep clone objects
2const original = {
3 name: 'Alice',
4 address: {
5 city: 'New York',
6 zip: '10001',
7 },
8 hobbies: ['reading', 'coding'],
9};
10
11const clone = structuredClone(original);
12
13// Modifications don't affect original
14clone.name = 'Bob';
15clone.address.city = 'Boston';
16clone.hobbies.push('gaming');
17
18console.log(original.name); // 'Alice'
19console.log(original.address.city); // 'New York'
20console.log(original.hobbies); // ['reading', 'coding']Supported Types#
1// Primitives
2structuredClone('hello'); // 'hello'
3structuredClone(42); // 42
4structuredClone(true); // true
5structuredClone(null); // null
6structuredClone(undefined); // undefined
7
8// Objects and Arrays
9structuredClone({ a: 1 }); // { a: 1 }
10structuredClone([1, 2, 3]); // [1, 2, 3]
11
12// Date objects
13const date = new Date();
14const clonedDate = structuredClone(date);
15console.log(clonedDate instanceof Date); // true
16console.log(clonedDate.getTime() === date.getTime()); // true
17
18// Regular expressions
19const regex = /hello/gi;
20const clonedRegex = structuredClone(regex);
21console.log(clonedRegex instanceof RegExp); // true
22console.log(clonedRegex.source); // 'hello'
23console.log(clonedRegex.flags); // 'gi'
24
25// Map and Set
26const map = new Map([['a', 1], ['b', 2]]);
27const clonedMap = structuredClone(map);
28console.log(clonedMap.get('a')); // 1
29
30const set = new Set([1, 2, 3]);
31const clonedSet = structuredClone(set);
32console.log(clonedSet.has(2)); // true
33
34// ArrayBuffer and TypedArrays
35const buffer = new ArrayBuffer(8);
36const view = new Uint8Array(buffer);
37view[0] = 255;
38
39const clonedBuffer = structuredClone(buffer);
40const clonedView = new Uint8Array(clonedBuffer);
41console.log(clonedView[0]); // 255Circular References#
1// Handles circular references automatically
2const obj = { name: 'circular' };
3obj.self = obj;
4
5const clone = structuredClone(obj);
6console.log(clone.self === clone); // true
7console.log(clone.self !== obj); // true
8
9// Complex circular structure
10const parent = { name: 'parent', children: [] };
11const child1 = { name: 'child1', parent };
12const child2 = { name: 'child2', parent };
13parent.children.push(child1, child2);
14
15const clonedParent = structuredClone(parent);
16console.log(clonedParent.children[0].parent === clonedParent); // trueNon-Cloneable Types#
1// Functions cannot be cloned
2try {
3 structuredClone({ fn: () => {} });
4} catch (e) {
5 console.log(e.message); // DataCloneError
6}
7
8// Symbols cannot be cloned
9try {
10 structuredClone({ sym: Symbol('test') });
11} catch (e) {
12 console.log(e.message); // DataCloneError
13}
14
15// DOM nodes cannot be cloned
16try {
17 structuredClone(document.body);
18} catch (e) {
19 console.log(e.message); // DataCloneError
20}
21
22// Property descriptors are not preserved
23const obj = {};
24Object.defineProperty(obj, 'readonly', {
25 value: 42,
26 writable: false,
27});
28
29const clone = structuredClone(obj);
30Object.getOwnPropertyDescriptor(clone, 'readonly').writable; // true
31
32// Prototype chain is not preserved
33class Person {
34 constructor(name) {
35 this.name = name;
36 }
37 greet() {
38 return `Hello, ${this.name}`;
39 }
40}
41
42const person = new Person('Alice');
43const clonedPerson = structuredClone(person);
44
45console.log(clonedPerson.name); // 'Alice'
46console.log(clonedPerson.greet); // undefined
47console.log(clonedPerson instanceof Person); // falseTransfer Option#
1// Transfer ownership of ArrayBuffers
2const buffer = new ArrayBuffer(1024);
3const view = new Uint8Array(buffer);
4view[0] = 42;
5
6// Transfer instead of clone
7const clonedBuffer = structuredClone(buffer, { transfer: [buffer] });
8
9console.log(buffer.byteLength); // 0 (detached)
10console.log(clonedBuffer.byteLength); // 1024
11
12// Transfer multiple transferables
13const buffer1 = new ArrayBuffer(100);
14const buffer2 = new ArrayBuffer(200);
15
16const obj = {
17 data1: buffer1,
18 data2: buffer2,
19};
20
21const clone = structuredClone(obj, {
22 transfer: [buffer1, buffer2],
23});
24
25// Original buffers are now detached
26console.log(buffer1.byteLength); // 0
27console.log(buffer2.byteLength); // 0Comparison with Other Methods#
1const original = {
2 name: 'test',
3 nested: { value: 42 },
4 date: new Date(),
5 regex: /pattern/g,
6 map: new Map([['key', 'value']]),
7};
8
9// JSON.parse/stringify - loses types
10const jsonClone = JSON.parse(JSON.stringify(original));
11console.log(jsonClone.date instanceof Date); // false (string)
12console.log(jsonClone.regex instanceof RegExp); // false (object)
13console.log(jsonClone.map instanceof Map); // false (object)
14
15// Spread operator - shallow only
16const spreadClone = { ...original };
17spreadClone.nested.value = 100;
18console.log(original.nested.value); // 100 (modified!)
19
20// Object.assign - shallow only
21const assignClone = Object.assign({}, original);
22assignClone.nested.value = 200;
23console.log(original.nested.value); // 200 (modified!)
24
25// structuredClone - deep clone with types
26const deepClone = structuredClone(original);
27deepClone.nested.value = 300;
28console.log(original.nested.value); // 42 (unchanged)
29console.log(deepClone.date instanceof Date); // true
30console.log(deepClone.regex instanceof RegExp); // true
31console.log(deepClone.map instanceof Map); // truePractical Examples#
1// State management
2class Store {
3 constructor(initialState) {
4 this.state = initialState;
5 this.history = [structuredClone(initialState)];
6 }
7
8 setState(updater) {
9 this.state = updater(structuredClone(this.state));
10 this.history.push(structuredClone(this.state));
11 }
12
13 undo() {
14 if (this.history.length > 1) {
15 this.history.pop();
16 this.state = structuredClone(this.history[this.history.length - 1]);
17 }
18 }
19}
20
21// Form data handling
22function handleFormSubmit(formData) {
23 // Clone to avoid mutations
24 const data = structuredClone(formData);
25
26 // Sanitize
27 data.email = data.email.toLowerCase().trim();
28
29 // Original formData unchanged
30 return submitToAPI(data);
31}
32
33// Cache with deep copies
34class DeepCache {
35 constructor() {
36 this.cache = new Map();
37 }
38
39 set(key, value) {
40 this.cache.set(key, structuredClone(value));
41 }
42
43 get(key) {
44 const value = this.cache.get(key);
45 return value ? structuredClone(value) : undefined;
46 }
47}
48
49// Immutable updates
50function updateUser(user, updates) {
51 const clone = structuredClone(user);
52 return Object.assign(clone, updates);
53}Error Handling#
1// Graceful handling of non-cloneable values
2function safeClone(value) {
3 try {
4 return structuredClone(value);
5 } catch (e) {
6 if (e.name === 'DataCloneError') {
7 console.warn('Value contains non-cloneable data:', e.message);
8 return fallbackClone(value);
9 }
10 throw e;
11 }
12}
13
14// Custom fallback
15function fallbackClone(value) {
16 // Handle functions by converting to null
17 return JSON.parse(
18 JSON.stringify(value, (key, val) => {
19 if (typeof val === 'function') return null;
20 if (typeof val === 'symbol') return null;
21 return val;
22 })
23 );
24}
25
26// Check if value is cloneable
27function isCloneable(value) {
28 try {
29 structuredClone(value);
30 return true;
31 } catch {
32 return false;
33 }
34}Performance Considerations#
1// structuredClone is slower than shallow methods
2// Use appropriately based on needs
3
4// For simple shallow copies, use spread
5const shallowCopy = { ...obj };
6
7// For deep nested structures, use structuredClone
8const deepCopy = structuredClone(obj);
9
10// For JSON-safe data, JSON methods may be faster
11const jsonCopy = JSON.parse(JSON.stringify(obj));
12
13// Benchmark
14function benchmark(fn, iterations = 1000) {
15 const start = performance.now();
16 for (let i = 0; i < iterations; i++) {
17 fn();
18 }
19 return performance.now() - start;
20}
21
22const testObj = { a: { b: { c: { d: 1 } } } };
23
24console.log('Spread:', benchmark(() => ({ ...testObj })));
25console.log('JSON:', benchmark(() => JSON.parse(JSON.stringify(testObj))));
26console.log('structuredClone:', benchmark(() => structuredClone(testObj)));Best Practices#
Usage:
✓ Use for deep cloning objects
✓ Use for immutable state updates
✓ Use when preserving types matters
✓ Use for complex nested structures
Benefits:
✓ Handles circular references
✓ Preserves Date, RegExp, Map, Set
✓ Native browser/Node.js support
✓ No external dependencies
Limitations:
✗ Cannot clone functions
✗ Cannot clone Symbols
✗ Cannot clone DOM nodes
✗ Loses prototype chain
Avoid:
✗ Using for shallow copies
✗ Cloning objects with functions
✗ Performance-critical loops
✗ When JSON suffices
Conclusion#
The structuredClone API provides native deep cloning with support for complex types like Date, RegExp, Map, and Set. Use it for immutable state management, caching, and any scenario requiring true deep copies. Remember it cannot clone functions, symbols, or DOM nodes, and consider performance for hot paths.