How to use the spread and rest operators
Same `...` syntax, opposite jobs. Spread EXPANDS an iterable/object into individual elements (copying arrays/objects, passing args). Rest COLLECTS multiple elements into one array (variadic params, destructuring). Both do shallow copies — nested objects are still shared.
... is one syntax with two opposite behaviors, and which one it is depends on where it appears.
Spread — expands
Spread takes an iterable/object and spreads it out into individual pieces. It appears in function calls, array literals, object literals:
// copy / merge arrays
const copy = [...arr];
const merged = [...a, ...b];
// copy / merge objects (later wins)
const newObj = { ...obj, name: "updated" };
// pass an array as individual arguments
Math.max(...[3, 1, 4]); // Math.max(3, 1, 4)Rest — collects
Rest takes multiple individual items and gathers them into one array/object. It appears in function parameters and destructuring (always last):
// variadic function params
function sum(...nums) { // nums is a real array
return nums.reduce((a, n) => a + n, 0);
}
// array destructuring
const [first, ...others] = [1, 2, 3]; // first=1, others=[2,3]
// object destructuring
const { id, ...rest } = user; // rest = everything except idThe rule of thumb
- On the right side of
=, or inside a call/literal → spread (expanding). - On the left side (destructuring) or in a parameter list → rest (collecting).
The critical caveat: shallow copy
Spread copies one level deep. Nested objects/arrays are still shared by reference:
const copy = { ...original };
copy.nested.x = 1; // ❌ also mutates original.nestedFor React state and immutable updates this matters — you must spread every level you change, or use structuredClone / a library for deep copies.
Common uses in React
setItems([...items, newItem]); // immutable add
setUser({ ...user, name: "new" }); // immutable update
<Component {...props} /> // spread propsThe framing
"Same ..., opposite jobs. Spread expands — in calls and literals, for copying/merging arrays and objects or passing array args. Rest collects — in parameter lists and destructuring, gathering items into one array. Rule: left side / params = rest, right side / calls / literals = spread. The gotcha that bites people is that spread is a shallow copy — nested references are shared, which is why immutable updates have to spread every level you touch."
Follow-up questions
- •Why is spread only a shallow copy, and when does that bite you?
- •How is rest different from the old `arguments` object?
- •How do you do a deep copy in modern JS?
- •Where can rest appear and where can't it?
Common mistakes
- •Assuming spread does a deep copy — nested objects stay shared.
- •Putting a rest element somewhere other than last.
- •Confusing which one is which because the syntax is identical.
- •Spreading a non-iterable (spread needs an iterable for arrays).
Performance considerations
- •Spread creates a new array/object each time — fine normally, but spreading large arrays repeatedly (e.g. in a reduce) is O(n²). Rest params allocate a real array, unlike the old arguments object.
Edge cases
- •Object spread copies only enumerable own properties (not prototype).
- •Spreading a string spreads it into characters.
- •Rest in destructuring with holes or default values.
- •Order matters in object spread — later keys overwrite earlier.
Real-world examples
- •Immutable state updates in React via {...state} and [...list].
- •Variadic utilities like sum(...nums) and prop forwarding <Comp {...props} />.