Apps Artificial Intelligence Cloud Computing CSS Cybersecurity Data Science Database Go JavaScript Linux Python Rust Software Engineering Web Development

Use structuredClone for Safe Deep Copying in JavaScript

3 min read .
Use structuredClone for Safe Deep Copying in JavaScript

Copying JavaScript values is easy until nested objects, dates, maps, sets, binary data, or circular references appear. The common JSON round trip works only for a limited subset of values and silently changes some data.

The built-in structuredClone() API provides a defined deep-cloning algorithm for many JavaScript data types.

Spread syntax is only a shallow copy

Object spread copies the first level:

const original = {
  profile: { name: "Ada" }
};

const copy = { ...original };
copy.profile.name = "Grace";

console.log(original.profile.name); // "Grace"

Both objects still reference the same nested profile. Spread syntax is useful when shallow copying is intentional, but it is not a general deep-copy mechanism.

JSON cloning is a serialization trick

This pattern is common:

const copy = JSON.parse(JSON.stringify(value));

It can be appropriate when data is intentionally JSON-shaped. It is not a transparent clone of arbitrary JavaScript values.

JSON serialization does not preserve structures such as Map, Set, typed arrays, circular references, or undefined properties in ordinary objects. Dates become strings rather than remaining Date instances.

If the boundary is genuinely JSON, serialize to JSON. If the goal is to clone an in-memory value, use a cloning API.

Basic structured cloning

Modern browsers and current Node.js releases provide structuredClone():

const original = {
  createdAt: new Date(),
  labels: new Set(["stable", "public"]),
  metadata: new Map([["retries", 2]])
};

const copy = structuredClone(original);

console.log(copy.createdAt instanceof Date); // true
console.log(copy.labels instanceof Set);     // true
console.log(copy.metadata instanceof Map);   // true

Supported nested values are cloned recursively, so later mutations to the clone do not modify the original object graph.

Circular references are supported

The structured clone algorithm tracks references:

const node = { name: "root" };
node.self = node;

const copy = structuredClone(node);

console.log(copy !== node);
console.log(copy.self === copy);

A JSON round trip cannot represent this graph.

Not every value is cloneable

Functions are not cloneable data:

structuredClone({ run() {} });

This throws a cloning error. Other host-specific values may also be unsupported.

That failure is often useful design feedback. Objects containing behavior, open resources, runtime handles, or framework internals may need an application-specific reconstruction strategy rather than generic deep copying.

Transfer large buffers when ownership can move

Some transferable objects can be moved rather than copied:

const buffer = new ArrayBuffer(1024 * 1024);

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

console.log(buffer.byteLength); // 0
console.log(moved.byteLength);  // 1048576

Transferring can avoid a byte-for-byte copy when moving large binary payloads between execution contexts. The trade-off is ownership: the original buffer becomes detached.

Deep copying has a cost

A deep clone walks an object graph and allocates new data. Repeating that on large structures creates CPU and garbage-collection pressure.

Before cloning defensively, consider whether the design would be clearer with:

  • immutable updates that copy only changed paths;
  • explicit ownership rules;
  • a smaller projection of the fields a consumer needs;
  • transfer semantics for large binary data.

Common pitfalls

Cloning to hide unclear ownership

If nobody knows which component may mutate an object, copying everywhere masks the underlying contract problem.

Expecting arbitrary class behavior to survive

Structured cloning copies supported data; it is not a universal constructor mechanism for application classes.

Choosing JSON because test fixtures are simple

Fixtures containing only strings and numbers do not expose the lossy behavior of JSON serialization. Test with real production-shaped values.

Conclusion

Use structuredClone() when you need an independent deep copy of supported JavaScript data. Use JSON serialization for JSON boundaries, spread syntax for shallow copies, transfer lists when ownership of large buffers can move, and explicit domain copying when application semantics matter. The useful question is not how to clone everything, but which data actually needs independent ownership.

Related Posts

chevron-up