DEV Community

Cover image for Spread vs Rest Operators in JavaScript
SATYA SOOTAR
SATYA SOOTAR

Posted on

Spread vs Rest Operators in JavaScript

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"]
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

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 };
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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" }
Enter fullscreen mode Exit fullscreen mode

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.

visual diagram

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)