Hello readers 👋, welcome to the 23rd blog in this JavaScript series!
In the last post, we discovered how async/await makes asynchronous code feel as natural as synchronous code. Today, we are going to talk about a pair of operators that look identical but do completely opposite jobs: the spread and rest operators.
Both use three dots (...), but one expands values out while the other collects values in. This tiny syntax is incredibly powerful and appears all over modern JavaScript. Let's unravel the mystery and see where each one shines.
What the spread operator does
Think of the spread operator as an unpacking tool. It takes an array or an object and spreads its elements or properties into individual pieces. It is like opening a box of chocolates and laying them all out on a table. The box (the array) gives you the chocolates (the individual items).
Spreading an array
Before spread, if you wanted to concatenate two arrays, you used concat. With spread, you just unpack the arrays inside a new one:
const fruits = ["apple", "banana"];
const moreFruits = ["orange", "mango"];
// Old way
const combinedOld = fruits.concat(moreFruits);
// Spread way
const combined = [...fruits, ...moreFruits];
console.log(combined); // ["apple", "banana", "orange", "mango"]
You can spread anywhere inside a new array. This makes it effortless to insert elements while copying:
const numbers = [10, 20, 30];
const withZero = [0, ...numbers, 40, 50];
console.log(withZero); // [0, 10, 20, 30, 40, 50]
Spreading an object
With objects, spread lets you copy properties from one object into another. This is incredibly handy for creating modified copies without mutating the original.
const user = {
name: "Satya",
age: 25
};
const updatedUser = {
...user,
role: "developer",
age: 26 // overrides the original age
};
console.log(updatedUser);
// { name: "Satya", age: 26, role: "developer" }
Notice that order matters. If you spread an object and then add a property with the same key later, the later one wins. This makes it easy to override defaults.
Real world example: updating state
In React and other frameworks, you often update state immutably. Before spread, you'd use Object.assign. Spread makes it way cleaner:
// Without spread
const updatedState = Object.assign({}, prevState, { count: prevState.count + 1 });
// With spread
const updatedState = { ...prevState, count: prevState.count + 1 };
What the rest operator does
If spread is unpacking, the rest operator is packing things together. It collects multiple values and condenses them into a single array. The same three dots, but used in a different context, like a vacuum cleaner gathering scattered crumbs into a bag.
Rest parameters in functions
You can use rest in a function's parameter list to capture any number of arguments into an array. This replaces the old arguments object (which was not a real array) and gives you a proper array with all the useful methods.
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(5, 10, 15, 20)); // 50
The ...numbers collects all passed arguments into the numbers array. You can name it anything you like.
Rest in destructuring
When destructuring an array or object, you can use rest to collect the remaining elements that were not explicitly captured.
const colors = ["red", "green", "blue", "yellow"];
const [first, second, ...restColors] = colors;
console.log(first); // "red"
console.log(second); // "green"
console.log(restColors); // ["blue", "yellow"]
Here, first and second capture the first two elements, and ...restColors collects the rest into a new array.
With objects:
const person = {
name: "Satya",
age: 25,
city: "Bhubaneswar",
country: "India"
};
const { name, age, ...address } = person;
console.log(name); // "Satya"
console.log(age); // 25
console.log(address); // { city: "Bhubaneswar", country: "India" }
The rest operator grabs the remaining properties and puts them into a fresh object. It's a clean way to separate specific properties from the rest of the object.
Differences between spread and rest
The easiest way to remember the difference is to look at where the three dots appear.
- Spread appears on the right side of an assignment or inside a function call. It expands an iterable into individual items or an object's properties into individual properties.
- Rest appears on the left side of an assignment (destructuring) or in function parameters. It collects multiple items into a single array or object.
Think of it like a box. Spread is taking things out of the box; rest is putting things back into the box.
| Context | Operator | Action |
|---|---|---|
Function call: func(...arr)
|
Spread | Expands array into individual arguments |
Array literal: [...arr]
|
Spread | Copies/expands elements into a new array |
Object literal: {...obj}
|
Spread | Copies/expands properties into a new object |
Function definition: function f(...args)
|
Rest | Collects arguments into an array |
Array destructuring: [a, ...rest]
|
Rest | Collects remaining elements into an array |
Object destructuring: {a, ...rest}
|
Rest | Collects remaining properties into an object |
Practical use cases that show both powers
Combining arrays and adding extras
const opening = ["Love", "Brave"];
const middle = ["Trust", "Peace"];
const closing = ["Hope", "Joy"];
const all = [...opening, "extra", ...middle, ...closing];
console.log(all);
Passing array elements as function arguments
You can spread an array when calling a function like Math.max, which expects comma-separated numbers:
const scores = [45, 89, 32, 98, 12];
const highest = Math.max(...scores);
console.log(highest); // 98
Without spread, you'd need Math.max.apply(null, scores), which is far less readable.
Creating a shallow copy of an object
When you need to duplicate an object without keeping the reference, spread gives a simple one-liner:
const original = { a: 1, b: 2 };
const copy = { ...original };
copy.a = 99;
console.log(original.a); // 1 (unchanged)
Be careful: it's a shallow copy, so nested objects are still referenced.
Collecting function arguments with rest, then spreading them again
Sometimes you capture arguments with rest, do some processing, and then spread them again:
function logWithPrefix(prefix, ...messages) {
const fullMessages = messages.map(msg => `${prefix}: ${msg}`);
console.log(...fullMessages);
}
logWithPrefix("INFO", "Server started", "Port 3000");
// INFO: Server started INFO: Port 3000
Splitting an object into a focused part and the rest
Useful when you want to remove specific keys from an object without mutating it:
const data = { id: 1, name: "Satya", password: "secret", role: "admin" };
const { password, ...safeData } = data;
console.log(safeData); // { id: 1, name: "Satya", role: "admin" }
We extracted the sensitive password and left the rest safe for logging or sending to the client.
Visualizing the concepts
Imagine you have a basket of fruits (an array). The spread operator is like emptying the basket onto a table, fruit by fruit. The items are now individual pieces you can move around.
On the other hand, the rest operator is like scooping up the leftover fruits on the table and putting them back into a new basket. You gather them into one container.
This mental image of unpacking vs packing keeps the two straight in your mind.
Conclusion
Spread and rest operators are small syntax additions that make a huge difference in how we write JavaScript. They both use ... but have precisely defined roles: spread expands, rest collects.
To quickly recap:
- Spread unpacks elements from an array or properties from an object into individual items. You use it in function calls, array literals, and object literals to copy, merge, or pass values.
- Rest collects multiple elements or properties into a single array or object. You use it in function parameters or destructuring patterns.
- The context determines the behavior: right-side or call-position means spread, left-side or parameter-position means rest.
- Both operators lead to cleaner, more expressive code, replacing verbose methods like
concat,apply, or manual cloning.
Mastering these two operators will make your code more readable and help you handle data transformations with elegance. You'll see them everywhere, from React state updates to utility functions.
Hope you found this helpful! If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X, where I post more about web development.

Top comments (0)