Back to Blog
Tips2026-04-05

structuredClone vs JSON.parse: The Right Way to Deep Copy in JavaScript

Compare structuredClone and JSON.parse(JSON.stringify()) for deep copying, with benchmarks and edge cases.

The Deep Copy Problem

Copying objects in JavaScript is tricky. Spread syntax and Object.assign only create shallow copies — nested objects still share references. For years, developers used \JSON.parse(JSON.stringify(obj))\ as a workaround. Now, \structuredClone()\ is the native solution. But which should you use, and when?

JSON Round-Trip: The Old Way

``javascript const original = { name: 'Alice', scores: [95, 87, 92], metadata: { created: '2024-01-01' } };

const copy = JSON.parse(JSON.stringify(original)); copy.scores.push(100); console.log(original.scores); // [95, 87, 92] — not affected `

This works for simple objects, but it has serious limitations:

`javascript // Things JSON round-trip BREAKS: const problematic = { date: new Date(), // Becomes a string regex: /hello/g, // Becomes {} map: new Map([[1, 2]]), // Becomes {} set: new Set([1, 2, 3]), // Becomes {} undef: undefined, // Property is removed entirely fn: () => 'hello', // Property is removed entirely inf: Infinity, // Becomes null nan: NaN // Becomes null };

const jsonCopy = JSON.parse(JSON.stringify(problematic)); console.log(jsonCopy.date); // "2024-01-01T..." (string, not Date) console.log(jsonCopy.map); // {} console.log(jsonCopy.undef); // undefined (property missing) `

structuredClone: The Modern Way

`javascript const original = { date: new Date(), regex: /hello/g, map: new Map([[1, 'one'], [2, 'two']]), set: new Set([1, 2, 3]), buffer: new ArrayBuffer(8), nested: { deep: { value: 42 } } };

const clone = structuredClone(original);

console.log(clone.date instanceof Date); // true console.log(clone.map instanceof Map); // true console.log(clone.set instanceof Set); // true console.log(clone.nested.deep.value); // 42

clone.map.set(3, 'three'); console.log(original.map.has(3)); // false — independent copy `

What structuredClone Cannot Clone

`javascript // These will throw DataCloneError: structuredClone({ fn: () => {} }); // Functions structuredClone({ el: document.body }); // DOM nodes structuredClone({ sym: Symbol('x') }); // Symbols

// Workaround: strip non-cloneable properties first function safeClone(obj, keysToOmit = []) { const filtered = Object.fromEntries( Object.entries(obj).filter(([key]) => !keysToOmit.includes(key)) ); return structuredClone(filtered); } ``

When to Use Which

| Feature | JSON round-trip | structuredClone | |---------|----------------|-----------------| | Date | String | Date | | Map/Set | Lost | Preserved | | undefined | Removed | Preserved | | Circular refs | Throws | Handled | | Performance (small) | Faster | Slightly slower | | Performance (large) | Slower | Faster |

Use structuredClone as your default for deep copying. Fall back to JSON round-trip only when you need to serialize to a string anyway, or when working in environments that don't support structuredClone (very old browsers).