Rest and Spread
What are rest and spread?
Rest and spread both use the ... syntax, but they do opposite jobs.
Rest gathers multiple values into one array or object.
Spread expands one array or object into multiple values or properties.
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3
Here, spread expands the numbers array into separate arguments.
Why this matters
The ... syntax appears in several common JavaScript patterns:
- passing arrays into functions
- writing functions that accept flexible arguments
- copying arrays
- combining arrays
- copying objects
- merging objects
- collecting the remaining values during destructuring
The syntax is small, but the meaning depends on where it appears.
The mental model
Use this rule of thumb:
- Rest gathers values together.
- Spread expands values outward.
function addAll(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(addAll(1, 2, 3)); // 6
...numbers gathers arguments into an array.
const numbers = [1, 2, 3];
console.log(addAll(...numbers)); // 6
...numbers spreads the array back out into separate arguments.
The same syntax is doing different work because it appears in a different place.
Rest parameters
Use a rest parameter when a function should accept any number of arguments.
function addAll(...numbers) {
let total = 0;
for (const number of numbers) {
total += number;
}
return total;
}
console.log(addAll(1, 2, 3)); // 6
console.log(addAll(5, 10, 15)); // 30
The rest parameter gathers all remaining arguments into the numbers array.
Rest parameters must come last:
function buildName(firstName, ...middleNames) {
return `${firstName} ${middleNames.join(" ")}`;
}
console.log(buildName("Alice", "Marie", "Lee")); // "Alice Marie Lee"
This works because ...middleNames gathers everything after firstName.
Spreading into function calls
Use spread when you have an array but a function expects separate arguments.
const numbers = [5, 10, 2, 8];
console.log(Math.max(...numbers)); // 10
console.log(Math.min(...numbers)); // 2
Without spread, Math.max(numbers) would pass the whole array as one argument.
Spread is also useful when calling your own functions:
function greet(firstName, lastName) {
return `Hello, ${firstName} ${lastName}!`;
}
const nameParts = ["Alice", "Smith"];
console.log(greet(...nameParts)); // "Hello, Alice Smith!"
Spreading arrays
Use spread to copy an array.
const original = [1, 2, 3];
const copy = [...original];
console.log(copy); // [1, 2, 3]
Use spread to combine arrays.
const first = [1, 2];
const second = [3, 4];
const combined = [...first, ...second];
console.log(combined); // [1, 2, 3, 4]
Use spread to add values while copying.
const items = ["a", "b"];
const updated = ["start", ...items, "end"];
console.log(updated); // ["start", "a", "b", "end"]
Array spread is shallow
Array spread creates a shallow copy.
The array itself is new, but nested objects and arrays are still shared.
const original = [{ name: "Alice" }];
const copy = [...original];
copy[0].name = "Bob";
console.log(original[0].name); // "Bob"
The array was copied, but the object inside it was not.
Spreading objects
Use object spread to copy an object.
const user = {
name: "Alice",
role: "admin"
};
const copy = { ...user };
console.log(copy); // { name: "Alice", role: "admin" }
Use object spread to merge objects.
const defaults = {
theme: "light",
language: "en"
};
const userPrefs = {
theme: "dark"
};
const settings = {
...defaults,
...userPrefs
};
console.log(settings); // { theme: "dark", language: "en" }
When two objects have the same property, the later spread wins.
In this example, userPrefs.theme replaces defaults.theme.
Updating objects with spread
Object spread is useful when you want a new object with one changed property.
const user = {
name: "Alice",
role: "member"
};
const adminUser = {
...user,
role: "admin"
};
console.log(adminUser); // { name: "Alice", role: "admin" }
console.log(user); // { name: "Alice", role: "member" }
The original object is unchanged.
Object spread is shallow
Object spread also creates a shallow copy.
const user = {
name: "Alice",
address: {
city: "Cleveland"
}
};
const copy = { ...user };
copy.address.city = "Boston";
console.log(user.address.city); // "Boston"
The top-level object was copied, but the nested address object was still shared.
Rest in array destructuring
Use rest in array destructuring to collect the remaining items.
const scores = [95, 88, 72, 64];
const [highest, secondHighest, ...otherScores] = scores;
console.log(highest); // 95
console.log(secondHighest); // 88
console.log(otherScores); // [72, 64]
Here, ...otherScores gathers the remaining array items.
Rest in object destructuring
Use rest in object destructuring to collect the remaining properties.
const user = {
id: 1,
name: "Alice",
role: "admin",
active: true
};
const { id, ...details } = user;
console.log(id); // 1
console.log(details); // { name: "Alice", role: "admin", active: true }
Here, ...details gathers the properties that were not already pulled out.
Common mistake: rest vs spread
The same ... syntax can mean rest or spread.
Ask what direction the values are moving.
function printNames(...names) {
console.log(names);
}
const users = ["Alice", "Bob"];
printNames(...users);
What happens:
...usersspreads the array into separate arguments....namesgathers those arguments back into an array.- The function prints
["Alice", "Bob"].
Common patterns
Copying before sorting
Use spread before a mutating method when the original array should stay unchanged.
const numbers = [10, 5, 40, 25];
const sorted = [...numbers].sort((a, b) => a - b);
console.log(numbers); // [10, 5, 40, 25]
console.log(sorted); // [5, 10, 25, 40]
Adding an item without changing the original array
const items = ["a", "b"];
const updatedItems = [...items, "c"];
console.log(items); // ["a", "b"]
console.log(updatedItems); // ["a", "b", "c"]
Updating one object property
const settings = {
theme: "light",
language: "en"
};
const updatedSettings = {
...settings,
theme: "dark"
};
console.log(updatedSettings); // { theme: "dark", language: "en" }
Best practices
- Remember the direction: rest gathers, spread expands.
- Use rest parameters for flexible argument lists.
- Use spread for shallow copies of arrays and objects.
- Remember that spread is shallow: nested objects and arrays are still shared.
- Put object spreads in the right order: later properties override earlier ones.
- Prefer spread for simple copying and merging when it keeps the code readable.
Summary
Rest and spread both use ..., but they do opposite jobs. Rest gathers values into an array or object. Spread expands arrays and objects into arguments, items, or properties.
Use rest parameters for flexible function arguments, array spread for copying and combining arrays, object spread for copying and merging objects, and destructuring rest when you need the remaining values.