Menu

JavaScript Object Spread: Clone, Merge, and Override with ...

How the object spread operator works in JavaScript — cloning, merging, overriding properties, and the shallow-copy gotcha that trips people up.

Spread Expands an Object Into Another

The object spread syntax — three dots in front of an object — copies that object's own enumerable properties into the surrounding object literal. It's the shortest path to cloning, merging, and producing modified copies without touching the original.

index.js
Output
Click Run to see the output here.

copy has the same keys and values as user, but it's a different object. Mutating one won't affect the other — at least at the top level. We'll come back to that caveat shortly.

The mental model: { ...obj } says "pour the properties of obj into this new object literal." Anything you write alongside the spread becomes part of the result.

Cloning and Overriding in One Step

The most common pattern is spreading an object and then adding or overriding a few properties. Later keys win, so you spread first and override after:

index.js
Output
Click Run to see the output here.

user is untouched. updated is a new object with role replaced. This immutable-update pattern shows up everywhere in modern JavaScript — React state updaters, Redux reducers, any code that prefers not to mutate in place.

Flip the order and you get the opposite behavior:

index.js
Output
Click Run to see the output here.

Here role: "guest" comes first, so user.role overrides it. Handy when you want defaults that the spread object can override.

Merging Objects

Spread two (or more) objects into a new literal to merge them. Later objects override earlier ones on conflicting keys:

index.js
Output
Click Run to see the output here.

theme and fontSize come from userPrefs; debug falls through from defaults. Three objects? Four? Same rule — read left to right, last write wins.

This is the modern replacement for Object.assign({}, defaults, userPrefs). Both do the same thing, but the spread version is easier to read and doesn't tempt you into the common bug of writing Object.assign(defaults, userPrefs) — which mutates defaults.

Spread Is a Shallow Copy

This is the part that catches people. Spread copies the top-level properties of an object. If a property's value is itself an object or an array, the reference is copied, not the contents.

index.js
Output
Click Run to see the output here.

Changing copy.address.city also changed user.address.city — because both objects share the same address object. The spread only gave us a new outer wrapper.

When you need to modify something nested, spread each level you want to change:

index.js
Output
Click Run to see the output here.

For a genuinely deep clone of arbitrary data, reach for structuredClone(obj). It handles nested objects, arrays, dates, maps, and sets — and it's built into every modern runtime.

Spread vs. Rest

They share the same three dots but do opposite things. Spread expands; rest collects.

index.js
Output
Click Run to see the output here.

The rule of thumb: if the ... is on the left side of an = (destructuring), it's rest. If it's inside an object or array literal on the right side, it's spread.

Removing a Property Immutably

Combining rest destructuring with spread gives you a clean way to produce a copy of an object with one key removed — no delete, no mutation:

index.js
Output
Click Run to see the output here.

tempToken is pulled out into its own variable (which you ignore), and everything else lands in safe. The original user is untouched.

What Spread Doesn't Copy

A few subtleties worth knowing:

  • Non-enumerable properties aren't copied. Most properties you create are enumerable by default, but properties defined with Object.defineProperty and certain built-ins aren't.
  • The prototype isn't copied. { ...instance } gives you a plain object, not an instance of the original class. Methods defined on the class prototype won't be on the copy.
  • Getters are evaluated. Spreading an object with a getter calls the getter once and stores the returned value as a normal property on the new object.
index.js
Output
Click Run to see the output here.

copy has x and y, but it's a plain object — distance lives on Point.prototype, which spread didn't touch. If you need to clone a class instance, the class usually has to provide its own clone method.

Next: Array Methods

Spread is one piece of the immutable-data toolkit. The other big piece is array methods — map, filter, reduce, and friends — which produce new arrays instead of mutating the originals. That's the next page.

Frequently Asked Questions

What does ...obj do in JavaScript?

Inside an object literal, ...obj copies all of that object's own enumerable properties into the new object. { ...user } produces a fresh object with the same keys and values as user. It's the standard way to clone or merge objects without mutating the originals.

How do I merge two objects in JavaScript?

Spread both into a new object literal: const merged = { ...a, ...b }. Properties from b override matching keys from a because later properties win. This is equivalent to Object.assign({}, a, b) but reads more cleanly.

Is object spread a deep copy?

No. Object spread is a shallow copy — it copies top-level properties, but nested objects and arrays are still shared by reference. Mutating copy.address.city will also mutate original.address.city. For a true deep copy, use structuredClone(obj).

What's the difference between spread and rest?

They look identical (...) but do opposite jobs. Spread expands an object or array into individual properties or elements: { ...user }. Rest collects leftovers into one variable: const { name, ...others } = user. Context tells you which one you're looking at.

Learn to code with Coddy

GET STARTED