PureDevTools

structuredClone() Reference

WeakMap errors, prototype loss, transferables, and Node/browser support with live examples. 100% client-side.

All processing happens in your browser. No data is sent to any server.
FeaturestructuredClone()JSON round-trip
Date✅ Date instance❌ string
Map / Set✅ Map / Set❌ {}
RegExp✅ RegExp instance❌ {}
NaN / Infinity✅ preserved❌ null
undefined✅ preserved❌ stripped
BigInt✅ preserved❌ throws
ArrayBuffer✅ cloned❌ throws
Circular refs✅ supported❌ throws
Functions❌ throws❌ silently stripped
structuredClone()Core API
structuredClone(value, options?)

Creates a deep clone of a value using the structured clone algorithm. The clone is a completely independent copy — modifications to the clone do not affect the original and vice versa. Handles nested objects, circular references, and many built-in types that JSON cannot handle.

transfer optionCore API
structuredClone(value, { transfer: [transferable, ...] })

The transfer option accepts an array of Transferable objects (e.g. ArrayBuffer, MessagePort, ReadableStream) that are moved rather than cloned into the result. After transfer, the original Transferable is detached and unusable — ownership is transferred to the clone.

Circular ReferencesCore API
structuredClone(valueWithCircularRef)

Unlike JSON.stringify(), structuredClone() correctly handles circular references — objects that contain references back to themselves or to ancestor objects in the same graph. The algorithm tracks visited objects and preserves the circular structure in the clone.

vs JSON.parse / JSON.stringifyCore API
structuredClone(value) vs JSON.parse(JSON.stringify(value))

The JSON round-trip (JSON.parse(JSON.stringify(obj))) is a common but flawed cloning idiom. It silently corrupts or discards Dates (converted to strings), undefined (stripped from objects), functions (stripped), Map/Set (converted to {}), RegExp (converted to {}), NaN/Infinity (converted to null), and throws on circular references.

DataCloneErrorCore API
// Throws: DOMException { name: 'DataCloneError' }

structuredClone() throws a DataCloneError (a DOMException with name 'DataCloneError') when it encounters an uncloneable value anywhere in the object graph: a Function, a Symbol value, a DOM node, a WeakMap/WeakSet, or other platform-specific objects.

You deep-copy an object with JSON.parse(JSON.stringify(obj)) — it works until the object contains a Date (becomes a string), a Map (becomes {}), a RegExp (becomes {}), undefined (deleted), or Infinity (becomes null). The JSON roundtrip is a lossy deep clone. structuredClone() handles Date, Map, Set, ArrayBuffer, circular references, and built-in Error objects correctly, but it still throws for functions, DOM nodes, WeakMap, and WeakSet, and it does not preserve custom prototype chains.

Quick Answers

Why This Reference (Not MDN)

MDN’s structuredClone() page covers the basics but it does not answer the most common implementation questions in one place: why WeakMap throws DataCloneError, whether the prototype chain survives, which Node.js versions support the API, and when the transfer option matters. This reference puts those answers first, then covers supported types, unsupported types, and the JSON round-trip comparison.

What Is structuredClone()?

structuredClone() is a native browser and Node.js global function that creates a deep clone of a value using the structured clone algorithm — the same algorithm used by the Web APIs for passing data between Workers, storing data in IndexedDB, and sending messages via postMessage().

const original = { user: { name: "Alice", scores: [1, 2, 3] } };
const clone = structuredClone(original);

clone.user.name = "Bob";
clone.user.scores.push(4);

console.log(original.user.name);   // Alice — unchanged
console.log(original.user.scores); // [1, 2, 3] — unchanged

Unlike the common JSON.parse(JSON.stringify(obj)) pattern, structuredClone() correctly handles Dates, RegExp, Map, Set, ArrayBuffer, circular references, NaN, Infinity, BigInt, and more.

Availability: Node.js 17+, Chrome 98+, Firefox 94+, Safari 15.4+, Deno 1.14+.


Syntax

structuredClone(value)
structuredClone(value, { transfer: [transferable, ...] })

Parameters

ParameterTypeDescription
valueanyThe value to deep-clone. Throws DataCloneError for unsupported types.
transferTransferable[]Optional. An array of transferable objects to move (not copy) into the clone.

Return Value

A deep clone of value. The clone is a completely independent copy.


Core API

Basic Deep Cloning

// Nested objects — clone is fully independent
const config = {
  database: {
    host: "localhost",
    port: 5432,
    options: { ssl: true, timeout: 30 },
  },
  features: ["auth", "cache"],
};

const backupConfig = structuredClone(config);
backupConfig.database.host = "backup.example.com";
backupConfig.features.push("monitoring");

console.log(config.database.host); // localhost (unchanged)
console.log(config.features);      // ["auth", "cache"] (unchanged)

Transfer Option — O(1) Ownership Transfer

The transfer option moves (rather than copies) Transferable objects. This is critical for performance with large binary data:

// Clone an object and transfer its large ArrayBuffer — no memory copy
const largeBuffer = new ArrayBuffer(50 * 1024 * 1024); // 50 MB
const data = { buffer: largeBuffer, metadata: { type: "image" } };

const clone = structuredClone(data, { transfer: [largeBuffer] });

// Original buffer is now detached — can no longer read it
console.log(largeBuffer.byteLength);      // 0 (detached)
console.log(clone.buffer.byteLength);     // 52428800 (50 MB)
console.log(clone.metadata.type);         // image

Transferable types include: ArrayBuffer, MessagePort, ReadableStream, WritableStream, TransformStream, OffscreenCanvas, ImageBitmap.

Circular References

structuredClone() handles circular references correctly — something that trips up both JSON.stringify() and many manual deep-clone implementations:

const node = { value: 1 };
const parent = { child: node };
node.parent = parent; // circular!

// JSON.stringify(parent); // throws TypeError

const clone = structuredClone(parent);
console.log(clone.child.parent === clone); // true — circular preserved
console.log(clone === parent);             // false — independent copy

Supported Types

Primitives — NaN, Infinity, and BigInt Included

// All primitives work — including ones JSON cannot represent
console.log(structuredClone(NaN));        // NaN   (JSON → null)
console.log(structuredClone(Infinity));   // Infinity (JSON → null)
console.log(structuredClone(undefined));  // undefined (JSON → omitted)
console.log(structuredClone(42n));        // 42n (JSON → throws)

Date

const original = { created: new Date("2024-01-15"), tags: ["js"] };
const clone = structuredClone(original);

console.log(clone.created instanceof Date);               // true
console.log(clone.created.toISOString());                 // 2024-01-15T00:00:00.000Z
// JSON round-trip: clone.created would be a string, not a Date

RegExp

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/gi;
pattern.lastIndex = 10;

const clone = structuredClone(pattern);
console.log(clone.source);    // (?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
console.log(clone.flags);     // gi
console.log(clone.lastIndex); // 0 — lastIndex is reset on the clone
// JSON round-trip: /regex/ becomes {}

Map and Set

const state = new Map([
  ["users", new Set(["alice", "bob", "carol"])],
  ["config", new Map([["theme", "dark"], ["lang", "en"]])],
]);

const clone = structuredClone(state);
console.log(clone instanceof Map);               // true
console.log(clone.get("users") instanceof Set);  // true
console.log(clone.get("users").has("alice"));    // true
// JSON round-trip: Map → {}, Set → {} (all data lost)

ArrayBuffer and TypedArray

// Clone typed arrays — underlying buffer is deep-copied
const matrix = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]);
const clonedMatrix = structuredClone(matrix);

clonedMatrix[0] = 99;
console.log(matrix[0]);       // 1 (original unchanged)
console.log(clonedMatrix[0]); // 99

// All TypedArray types are supported:
// Int8Array, Uint8Array, Uint8ClampedArray
// Int16Array, Uint16Array, Int32Array, Uint32Array
// Float32Array, Float64Array, BigInt64Array, BigUint64Array

Error Objects

const original = new TypeError("validation failed", {
  cause: new RangeError("value out of bounds"),
});
const clone = structuredClone(original);

console.log(clone instanceof TypeError);       // true
console.log(clone.message);                    // validation failed
console.log(clone.cause instanceof RangeError); // true
console.log(clone.cause.message);              // value out of bounds

Unsupported Types

These types throw a DataCloneError or lose data when cloned:

TypeBehaviorReason
FunctionThrows DataCloneErrorCannot serialize executable code
DOM nodesThrows DataCloneErrorPlatform-specific native objects
Symbol valueThrows DataCloneErrorSymbols are unique, cannot be recreated
Symbol-keyed propertiesSilently droppedNot enumerable to the algorithm
WeakMap / WeakSetThrows DataCloneErrorContents not enumerable by design
WeakRefThrows DataCloneErrorCannot serialize weak references
Class instancesPrototype lostOnly own enumerable data is cloned
Getters / SettersValue capturedAccessor descriptors cannot be serialized

Why WeakMap and WeakSet Throw DataCloneError

WeakMap and WeakSet cannot be cloned because their entries are not enumerable — that’s by design. A WeakMap holds its keys weakly so the garbage collector can reclaim them when no other reference exists. The structured clone algorithm cannot iterate over WeakMap entries to copy them, so it throws DataCloneError.

const wm = new WeakMap();
const key = { id: 1 };
wm.set(key, "secret");

try {
  structuredClone(wm); // throws DataCloneError
} catch (e) {
  console.log(e.name);    // DataCloneError
  console.log(e.message); // "WeakMap object could not be cloned"
}

Workaround: If you need to clone data that includes WeakMap-like behavior, extract the entries you need into a regular Map before cloning, then reconstruct the WeakMap from the cloned Map.

Custom Class Instances and Prototype Preservation

structuredClone() does not preserve the prototype chain. If you clone a class instance, the result is a plain Objectinstanceof checks will fail, and prototype methods will be unavailable.

class User {
  constructor(name) { this.name = name; }
  greet() { return `Hi, I'm ${this.name}`; }
}

const alice = new User("Alice");
const clone = structuredClone(alice);

console.log(clone.name);             // "Alice" — data is copied
console.log(clone instanceof User);  // false — prototype lost!
console.log(clone.greet);            // undefined — methods gone

Workaround: Implement a clone() method on your class, or use a serialization pattern:

class User {
  constructor(name) { this.name = name; }
  clone() { return new User(this.name); }
  toJSON() { return { name: this.name }; }
  static fromJSON(data) { return new User(data.name); }
}

Handling Uncloneable Data

// Option 1: strip functions before cloning
const { handler, ...cloneable } = appState;
const stateClone = structuredClone(cloneable);

// Option 2: wrap in try/catch
function safeClone(value, fallback = null) {
  try {
    return structuredClone(value);
  } catch {
    return fallback;
  }
}

// Option 3: toJSON() / fromJSON() pattern for class instances
class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  toJSON() { return { x: this.x, y: this.y }; }
  static fromJSON(obj) { return new Point(obj.x, obj.y); }
  clone() { return Point.fromJSON(structuredClone(this.toJSON())); }
}

const p = new Point(3, 4);
const pClone = p.clone();
console.log(pClone instanceof Point); // true
console.log(pClone.x);                // 3

structuredClone() vs JSON.parse/JSON.stringify

FeaturestructuredClone()JSON.parse/stringify
DatePreserved as DateConverted to string
Map / SetFully preservedConverted to {}
RegExpPattern and flags preserved; lastIndex resetsConverted to {}
undefinedPreservedStripped from objects
NaN / InfinityPreservedConverted to null
BigIntPreservedThrows
ArrayBufferDeep-copiedThrows
Circular referencesSupportedThrows
FunctionsThrows DataCloneErrorSilently stripped
Output typeValueJSON string
PerformanceFaster for complex objectsFaster for simple objects

Rule of thumb: Use structuredClone() for cloning data in memory. Use JSON.parse/stringify when you need a portable string representation or must support very old environments.


Use Cases

Immutable State Management

// Create an independent snapshot before mutation
function updateUser(state, id, updates) {
  const draft = structuredClone(state);
  const user = draft.users.find(u => u.id === id);
  if (user) Object.assign(user, updates);
  return draft; // original state unchanged
}

const state = { users: [{ id: 1, name: "Alice", role: "admin" }] };
const newState = updateUser(state, 1, { role: "moderator" });
console.log(state.users[0].role);    // admin (unchanged)
console.log(newState.users[0].role); // moderator

Worker Communication

// structuredClone() uses the same algorithm as postMessage()
// Testing locally before sending to a Worker:
function sendToWorker(worker, data) {
  try {
    structuredClone(data); // verify it is cloneable
    worker.postMessage(data);
  } catch (e) {
    console.error("Data not transferable:", e.message);
  }
}

Deep Default Merging

function withDefaults(options, defaults) {
  return { ...structuredClone(defaults), ...structuredClone(options) };
}

const defaults = {
  timeout: 5000,
  retry: { count: 3, delay: 1000 },
  headers: new Map([["Content-Type", "application/json"]]),
};

const opts = withDefaults({ timeout: 10000 }, defaults);
console.log(opts.timeout);                       // 10000
console.log(opts.retry.count);                   // 3
console.log(opts.headers instanceof Map);         // true
opts.retry.delay = 2000;
console.log(defaults.retry.delay);               // 1000 (unchanged)

Frequently Asked Questions

Is structuredClone() faster than JSON.parse/JSON.stringify?

For deeply nested structures or structures containing complex types (Map, Set, Date, ArrayBuffer), yes — structuredClone() is typically faster because it avoids string serialization and parsing. For simple flat plain-object data, the performance difference is negligible. The more important consideration is correctness: structuredClone() is correct for all supported types, while the JSON round-trip silently corrupts data.

Can I deep-clone a class instance with structuredClone()?

You can pass a class instance, but the clone will be a plain Object — it loses its prototype, so instanceof checks fail and prototype methods are unavailable. To clone class instances with prototype intact, implement a clone() method on the class or use a serialization/deserialization pattern (toJSON() + fromJSON()).

What happens to non-enumerable properties?

They are silently dropped. structuredClone() only copies own enumerable string-keyed properties. Use Object.getOwnPropertyDescriptors() and Object.defineProperties() if you need to preserve non-enumerable properties.

Can structuredClone() be used in a Service Worker?

Yes. structuredClone() is available in all worker contexts including Service Workers, Web Workers, and Shared Workers. It shares the same algorithm used by postMessage().

How do I deep-clone a DOM node?

Use Node.cloneNode(true) for a deep clone (with all child nodes) or Node.cloneNode(false) for a shallow clone. structuredClone() throws a DataCloneError for DOM nodes.

Does structuredClone() clone the prototype?

No. The prototype chain is not preserved. Custom class instances become plain objects. If you need prototype preservation, use Object.assign(Object.create(Object.getPrototypeOf(src)), src) for shallow cloning with prototype.

Related Tools

More JavaScript Tools