DocsHub
Objects

Spread and Rest

Learn how the spread and rest operators work with objects in JavaScript.

Spread and Rest

We already covered spread and rest with arrays in detail. With objects they work the same way — same ... syntax, same idea — but applied to properties instead of array items.

  • Spread — expands an object's properties into another object
  • Rest — collects remaining properties into a new object

Spread With Objects

Copying an Object

The most common use — making a real independent copy of an object.

const user = { name: "Ali", age: 22 };
const copy = { ...user };

copy.name = "Sara";

console.log(user.name); // Ali — unchanged
console.log(copy.name); // Sara

Without spread, copy = user would make both point to the same object in memory.

Merging Objects

Combine two or more objects into one new object.

const personal = { name: "Ali", age: 22 };
const professional = { role: "Developer", company: "DocsHub" };

const profile = { ...personal, ...professional };

console.log(profile);
// { name: "Ali", age: 22, role: "Developer", company: "DocsHub" }

Overwriting Properties

When spreading multiple objects with the same key, the last one wins.

const defaults = { theme: "light", language: "en", fontSize: 14 };
const userSettings = { theme: "dark", fontSize: 16 };

const settings = { ...defaults, ...userSettings };

console.log(settings);
// { theme: "dark", language: "en", fontSize: 16 }

theme and fontSize from userSettings overwrite the ones from defaults. language stays because userSettings does not have it.

This is the standard pattern for applying user preferences over default settings.

Adding or Updating Properties

Spread and add new properties at the same time — without touching the original.

const user = { name: "Ali", age: 22 };

// Add a new property
const withCity = { ...user, city: "Lahore" };
console.log(withCity); // { name: "Ali", age: 22, city: "Lahore" }
console.log(user);     // { name: "Ali", age: 22 } — unchanged

// Update an existing property
const older = { ...user, age: 23 };
console.log(older); // { name: "Ali", age: 23 }
console.log(user);  // { name: "Ali", age: 22 } — unchanged

The property after the spread overwrites the one inside it. Order matters — put the override after the spread.

// ❌ Wrong order — spread overwrites your value
const wrong = { age: 23, ...user };
console.log(wrong.age); // 22 — user.age spread over your 23

// ✅ Correct order — your value overwrites the spread
const correct = { ...user, age: 23 };
console.log(correct.age); // 23

Removing a Property

Spread cannot remove a property directly — but combine it with rest destructuring and you can:

const user = { name: "Ali", age: 22, password: "secret123", city: "Lahore" };

const { password, ...safeUser } = user;

console.log(safeUser);
// { name: "Ali", age: 22, city: "Lahore" }
console.log(password); // secret123 — separated out, not in safeUser

You destructure out the property you want to remove, and collect the rest into a new clean object. This is the standard way to exclude a sensitive field before sending data somewhere.


Rest With Objects

Rest in object destructuring collects all properties that were not explicitly destructured into a new object.

const user = { name: "Ali", age: 22, city: "Lahore", role: "admin" };

const { name, role, ...details } = user;

console.log(name);    // Ali
console.log(role);    // admin
console.log(details); // { age: 22, city: "Lahore" }

Real use — separating concerns

const request = {
  method: "POST",
  url: "/api/users",
  body: { name: "Ali", email: "ali@example.com" },
  headers: { "Content-Type": "application/json" },
  timeout: 5000
};

const { method, url, ...config } = request;

console.log(method); // POST
console.log(url);    // /api/users
console.log(config); // { body: {...}, headers: {...}, timeout: 5000 }

method and url are pulled out for routing. Everything else stays together in config to be passed along.


Shallow Copy Warning

Both spread and Object.assign() create shallow copies. If an object has nested objects, those nested ones are still shared by reference.

const user = {
  name: "Ali",
  address: {
    city: "Lahore",
    country: "Pakistan"
  }
};

const copy = { ...user };

copy.name = "Sara";           // ✅ only changes copy
copy.address.city = "Karachi"; // ❌ changes both — address is shared

console.log(user.name);         // Ali — unchanged
console.log(user.address.city); // Karachi — changed!

The top level is copied. But address is an object — and both user and copy point to the same address object in memory.

For a true deep copy of a simple object with no functions or special values:

const deepCopy = JSON.parse(JSON.stringify(user));

This works for plain data objects. For complex objects with functions, dates, or circular references — you need a library like Lodash's cloneDeep. We cover this in the Advanced section.

Always be aware of shallow vs deep copying when working with nested objects. Spreading looks like a full copy but nested objects are still shared.


A Real Example — Updating State

In React and similar frameworks, you never mutate state directly — you always create a new object with the updated value. Spread makes this clean and easy.

const state = {
  user: "Ali",
  theme: "light",
  notifications: true,
  language: "en"
};

// Toggle theme
const newState = { ...state, theme: state.theme === "light" ? "dark" : "light" };
console.log(newState.theme); // dark
console.log(state.theme);    // light — original unchanged
// Update a nested property without mutating
const userProfile = {
  name: "Ali",
  settings: {
    theme: "light",
    language: "en"
  }
};

const updated = {
  ...userProfile,
  settings: {
    ...userProfile.settings,
    theme: "dark"
  }
};

console.log(updated.settings.theme);       // dark
console.log(userProfile.settings.theme);   // light — unchanged

Spread the outer object, then spread the nested object with the override. This pattern is everywhere in real React code.


Spread vs Object.assign()

Both merge objects — spread is the modern preferred way.

const a = { x: 1 };
const b = { y: 2 };

// Object.assign — old way
const merged1 = Object.assign({}, a, b);

// Spread — modern way
const merged2 = { ...a, ...b };

console.log(merged1); // { x: 1, y: 2 }
console.log(merged2); // { x: 1, y: 2 }
Object.assign()Spread ...
SyntaxVerboseClean
Returns new objectOnly with {} as first argAlways
Modifies targetYes if target is existing objectNever
Modern JavaScriptOld but validPreferred

Use spread in modern code — it is shorter, clearer, and never accidentally mutates an existing object.


Summary

  • Spread expands object properties into another object — { ...obj }
  • Use spread to copy objects without mutation
  • Use spread to merge objects — last property wins on conflicts
  • Use spread to add or update properties — put overrides after the spread
  • Combine rest destructuring with spread to remove a property — const { key, ...rest } = obj
  • Rest in destructuring collects remaining properties into a new object
  • Spread creates shallow copies — nested objects are still shared by reference
  • Use { ...outer, nested: { ...outer.nested, key: value } } to safely update nested properties

On this page