Spread vs Rest Operators in JavaScript

If you've been writing JavaScript for a while, you've probably seen ... pop up in code and wondered — wait, what is this doing here? Sometimes it's expanding things, sometimes it's collecting things. Same syntax, two very different jobs.
Let's break it down simply.
1. What Spread Operator Does
The spread operator expands an array or object into individual pieces.
Think of it like unpacking a box — you take everything out and lay it on the table.
const fruits = ["apple", "banana", "mango"];
console.log(...fruits); // apple banana mango
You're not passing an array anymore — you're passing three separate values. That's what spread does. It opens things up.
2. What Rest Operator Does
The rest operator does the opposite — it collects multiple values and packs them into one array.
Think of it like gathering scattered things into a bag.
function total(...numbers) {
return numbers.reduce((sum, n) => sum + n, 0);
}
total(1, 2, 3, 4); // 10
Here, no matter how many arguments you pass, rest grabs them all and puts them in a numbers array. Clean and flexible.
3. Differences Between Spread and Rest
They look the same (...) but they work in opposite directions:
| Spread | Rest | |
|---|---|---|
| What it does | Expands values outward | Collects values inward |
| Where it's used | Function calls, array/object literals | Function parameters, destructuring |
| Think of it as | Unpacking | Packing |
A quick way to tell them apart — if ... is on the receiving end (like in a function parameter), it's rest. If it's on the giving end (like when you're passing or copying), it's spread.
4. Using Spread with Arrays and Objects
Combining arrays:
const a = [1, 2, 3];
const b = [4, 5, 6];
const combined = [...a, ...b]; // [1, 2, 3, 4, 5, 6]
Copying an array without linking it:
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] — untouched
console.log(copy); // [1, 2, 3, 4]
Spreading into objects:
const user = { name: "Rohan", age: 25 };
const updatedUser = { ...user, city: "Mumbai" };
// { name: "Rohan", age: 25, city: "Mumbai" }
This is super useful when you want to add or override fields without mutating the original object.
5. Practical Use Cases
Merging config objects (common in React and Node):
const defaultConfig = { theme: "light", lang: "en" };
const userConfig = { lang: "hi", fontSize: 16 };
const finalConfig = { ...defaultConfig, ...userConfig };
// { theme: "light", lang: "hi", fontSize: 16 }
User config wins over defaults — simple and readable.
Passing array items to a function:
const nums = [5, 10, 3];
Math.max(...nums); // 10
Without spread, Math.max([5, 10, 3]) would return NaN. Spread fixes that.
Rest in destructuring — grab what you need, collect the rest:
const [first, second, ...remaining] = [10, 20, 30, 40, 50];
console.log(first); // 10
console.log(remaining); // [30, 40, 50]
Wrapping Up
Same three dots, two different purposes:
Spread = open it up, expand values outward
Rest = pack it up, collect values inward
Once this clicks, you'll start seeing ... everywhere in modern JavaScript — and it'll make complete sense every time.




