Skip to content
JavaScript js objects 4 min read

Spread & Rest with Objects

The spread (...) and rest (...) operators look identical but do opposite jobs. Spread expands an object’s own enumerable properties into a new object literal, making it the cleanest way to clone and merge objects. Rest, used in destructuring, collects leftover properties into a fresh object. Both arrived for objects in ES2018 and are now the idiomatic way to work with object copies in modern JavaScript.

Cloning an object with spread

Placing ...obj inside a new object literal copies every own enumerable property into the new object. The result is a brand-new object with the same key/value pairs — mutating the copy never touches the original.

const user = { id: 1, name: "Ada" };
const copy = { ...user };

copy.name = "Grace";

console.log(user.name); // unchanged
console.log(copy.name);
console.log(copy === user);

Output:

Ada
Grace
false

This replaces older patterns like Object.assign({}, user). The spread form is more concise and works inside any object literal alongside other properties.

Merging objects and override order

You can spread multiple objects into one literal. When two sources share a key, the last one wins — properties are applied left to right, and later values overwrite earlier ones. This makes spread perfect for applying defaults.

const defaults = { theme: "light", fontSize: 14, debug: false };
const userPrefs = { theme: "dark", fontSize: 16 };

const settings = { ...defaults, ...userPrefs };
console.log(settings);

Output:

{ theme: 'dark', fontSize: 16, debug: false }

Because order matters, place the object whose values should take precedence last. You can also mix spreads with literal properties to override or add a single key inline.

const settings = { ...defaults, ...userPrefs, debug: true };

Tip: To apply defaults behind user input, spread the defaults first and the user object last. Reversing the order would let stale defaults clobber real values.

The shallow-copy caveat

Spread performs a shallow copy. Top-level primitive values are copied, but nested objects and arrays are copied by reference — the clone and the original still point to the same nested object. Mutating that nested value affects both.

const original = {
  name: "Ada",
  roles: ["admin"],
  address: { city: "London" },
};

const clone = { ...original };
clone.address.city = "Paris";

console.log(original.address.city); // also changed!

Output:

Paris

For an independent deep copy, use the built-in structuredClone() (available in modern browsers and Node 17+), which recursively duplicates nested structures.

const deep = structuredClone(original);
deep.address.city = "Berlin";

console.log(original.address.city);
console.log(deep.address.city);

Output:

London
Berlin
TechniqueDepthNotes
{ ...obj }ShallowFast, copies own enumerable props
Object.assign({}, obj)ShallowTriggers setters on the target
structuredClone(obj)DeepNo functions/DOM nodes; clones Maps, Sets, Dates
JSON.parse(JSON.stringify(obj))DeepLoses functions, undefined, Date becomes string

Rest in object destructuring

Rest does the inverse of spread. In a destructuring pattern, a trailing ...name gathers every property that wasn’t already named into a new object. It is ideal for pulling out one field while keeping “everything else” intact.

const user = { id: 1, name: "Ada", password: "s3cr3t", role: "admin" };

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

console.log(safeUser);

Output:

{ id: 1, name: 'Ada', role: 'admin' }

The rest property must come last in the pattern, and like spread it produces a shallow copy of the remaining own enumerable properties. This pattern shines for removing keys immutably (e.g. stripping a password before sending a response) without mutating the source object.

You can combine renaming, defaults, and rest in a single destructure:

function configure({ retries = 3, ...rest }) {
  console.log(retries, rest);
}

configure({ retries: 5, timeout: 1000, cache: true });
configure({ timeout: 500 });

Output:

5 { timeout: 1000, cache: true }
3 { timeout: 500 }

Spread vs rest at a glance

AspectSpreadRest
Where it appearsInside an object literalInside a destructuring pattern
DirectionExpands properties outCollects properties in
PositionAnywhere in the literalMust be last
ProducesA new merged objectA new object of leftovers

Best Practices

  • Reach for { ...obj } to copy or merge instead of mutating an existing object — favoring immutable updates makes state easier to reason about.
  • Remember override order: put the highest-priority source object last in the spread.
  • Treat spread as shallow; use structuredClone() when nested objects must be fully independent.
  • Use rest destructuring (const { secret, ...rest } = obj) to omit keys without mutating the original.
  • Keep the rest element last in any destructuring pattern, or the syntax will throw.
  • Avoid spreading inside hot loops over huge objects; each spread allocates a new object and copies every key.
Last updated June 1, 2026
Was this helpful?