structuredClone()

Creates a deep copy of a value, preserving Map, Set, Date, typed arrays and circular references.

Signature
structuredClone(value)
structuredClone(value, options)
  • Added in JavaScript Baseline March 2022 (WHATWG HTML; Node.js 17)

Parameters

ParameterTypeDescription
$valueanyThe object to be cloned. It must be a structured-cloneable type.
$optionsoptionalobject= undefinedAn options object.
$options.transferoptionalTransferable[]= undefinedAn array of transferable objects to be moved rather than copied. Transferred objects are detached from the original and attached to the clone.

Return value

anyA deep copy of the given value, with circular references and supported built-in types preserved.

Errors

Throws a DataCloneError DOMException if any part of the input is not serializable — functions, class instances' prototypes, DOM nodes, WeakMap, Symbol and Error subclass details are among the values it rejects or flattens.

Changelog

  • PHP Node.js 17Exposed as a global, matching browsers. Earlier Node versions have no structuredClone.

structuredClone() runs the structured clone algorithm — the same machinery that moves data between a page and a worker — as a plain function call. It gives you a genuine deep copy with none of the compromises of the JSON.parse(JSON.stringify(x)) trick.

What it handles that JSON does not

  • Map, Set, Date, RegExp, ArrayBuffer, typed arrays and BigInt.
  • Circular and shared references — an object referenced twice in the source is one object in the clone, not two.
  • undefined values and array holes, which JSON drops or nulls.

What it refuses

Functions, methods, getters, Symbol, WeakMap and DOM nodes cause a DataCloneError. Class instances survive but arrive as plain objects: the prototype is not carried across, so instanceof fails and methods are gone. Property descriptors are flattened too — a getter becomes its evaluated value, and writable: false is not preserved.

When to reach for it

Snapshotting mutable state before an operation you might need to undo, isolating a caller's object from later mutation, or building an independent copy for a worker. For an object graph you own that is only plain data, it is both faster and more faithful than serialising through JSON.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Snapshot application state before a risky mutation so you can restore it.
  • Defensively copy an options object a caller passed in, so later mutations cannot reach you.
  • Duplicate a graph containing Maps, Sets and Dates that JSON round-tripping would destroy.

Frequently asked questions

Why does structuredClone throw DataCloneError?

Because some part of the value is not structured-cloneable — most often a function, a method on a plain object, a Symbol, a WeakMap, or a DOM node. Strip or replace those properties before cloning, or copy the data-only subtree instead.

Is structuredClone better than JSON.parse(JSON.stringify(obj))?

Yes for most cases: it preserves Date, Map, Set, RegExp, typed arrays, BigInt, undefined and circular references, all of which the JSON trick destroys or crashes on. The JSON approach is only preferable when you specifically want a JSON-shaped result.

Does structuredClone copy class instances and their methods?

No — it copies own enumerable data properties into a plain object and drops the prototype, so methods and getters are gone and instanceof returns false. Rehydrate with a constructor call or a fromJSON-style factory if you need the class back.

More examples

Against the JSON round trip

The clone keeps a real Date, a real Set, the undefined key and the BigInt. The JSON round trip turns the Date into a string, the Set into {}, drops the undefined key entirely, and would throw outright on the BigInt.

Example · javascript
const src = {
  when: new Date(0),
  set: new Set([1, 2]),
  nothing: undefined,
  big: 10n,
};

console.log(structuredClone(src));
console.log(JSON.parse(JSON.stringify({ ...src, big: 0 })));

Prototypes and getters are lost

Only own enumerable data properties survive. The accessor `length` is not an own property so it vanishes, `toString` falls back to Object.prototype's, and the instance is no longer a Point.

Example · javascript
class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  get length() { return Math.hypot(this.x, this.y); }
  toString() { return `(${this.x}, ${this.y})`; }
}

const p = new Point(3, 4);
const c = structuredClone(p);

console.log(p instanceof Point, c instanceof Point); // true false
console.log(c);                                       // { x: 3, y: 4 }
console.log(typeof c.toString, c.length);             // function undefined
console.log(Object.getPrototypeOf(c) === Object.prototype); // true

Transferring a buffer instead of copying it

With `transfer`, the underlying ArrayBuffer is moved rather than duplicated: the clone owns the memory and the original view is left detached with zero length. Useful for large binary payloads, destructive for anything you still need.

Example · javascript
const data = new Uint8Array(1024);
data[0] = 42;

const moved = structuredClone(data, { transfer: [data.buffer] });

console.log(moved[0], moved.byteLength); // 42 1024
console.log(data.byteLength);            // 0 — original detached
console.log(data.buffer.detached);       // true

Discussion

  • Be the first to comment on this lesson.