structuredClone() is the native browser and Node.js API for deep-cloning objects — it handles circular references, typed arrays, Maps, Sets, and Dates correctly, replacing the fragile JSON.parse(JSON.stringify()) hack used in WordPress JavaScript code.
Problem: JavaScript code that spreads or copies objects before mutation — state updates in a WordPress admin SPA, immutable reducer patterns — uses JSON.parse(JSON.stringify(obj)) to deep clone, which fails on Date, Map, RegExp, and circular references.
Solution: Use the native structuredClone() function — it handles all serialisable types correctly, including Date, Map, Set, ArrayBuffer, and circular references. It is available in all modern browsers and Node.js 17+, requires no library, and is significantly faster than the JSON round-trip approach.
The examples below compare cloning strategies, show how structuredClone() handles complex types, and demonstrate immutable update patterns for WordPress block editor state management.
// ── Old fragile deep clone ──
const clone1 = JSON.parse( JSON.stringify( obj ) );
// Problems: loses Date objects (converts to string), undefined values, functions,
// Map/Set (converts to {}), and throws on circular references.
// ── structuredClone() — native, correct ──
const original = {
title: 'My Post',
date: new Date( '2024-08-02' ), // Date preserved
tags: new Set( ['wordpress', 'php'] ), // Set preserved
meta: new Map( [['key', 'value']] ), // Map preserved
nested: { count: 42 },
};
const clone = structuredClone( original );
clone.nested.count = 99;
console.log( original.nested.count ); // 42 — original unchanged
console.log( clone.date instanceof Date ); // true
console.log( clone.tags instanceof Set ); // true
// What structuredClone CANNOT clone (throws DataCloneError):
// - Functions
// - DOM nodes
// - class instances (prototype is lost — becomes plain object)
// - WeakMap / WeakSet
Immutable update patterns for Gutenberg block editor state:
// In a Gutenberg block's edit() function, attributes must not be mutated.
// Use structuredClone to create a deep copy before modifying.
function updateNestedAttribute( attributes, setAttributes ) {
// BAD — mutates the existing attributes object:
// attributes.settings.display = 'grid';
// GOOD — deep clone first, then modify:
const newAttributes = structuredClone( attributes );
newAttributes.settings.display = 'grid';
setAttributes( newAttributes );
}
// Immutable array update patterns (no mutation):
const items = [ { id: 1, done: false }, { id: 2, done: false } ];
// Toggle item 1 — spread for shallow, structuredClone for deep
const updated = items.map( item =>
item.id === 1 ? { ...item, done: true } : item
);
// Remove an item immutably
const filtered = items.filter( item => item.id !== 1 );
// Add an item immutably
const appended = [ ...items, { id: 3, done: false } ];
// For deeply nested structures in a Redux-style store (wp.data):
const updateDeepState = ( state, blockId, newValue ) =>
structuredClone( {
...state,
blocks: state.blocks.map( b =>
b.clientId === blockId ? { ...b, attributes: { ...b.attributes, value: newValue } } : b
),
} );
NOTE: structuredClone() is available in all modern browsers and Node.js 17+. For class instances where the prototype matters, use a custom clone() method or a library like lodash.cloneDeep — but for plain data objects (the vast majority of WordPress JS state), structuredClone() is the correct choice.