Skip to main content

Command Palette

Search for a command to run...

Spread & Rest

A practical guide to cloning objects, merging arrays, and handling infinite function arguments.

Published
6 min read

JavaScript has a syntax that looks identical in two different situations but means the opposite each time. Three dots: .... Depending on where you put them, they either expand something out or collect things in.

When they expand, that's the spread operator. When they collect, that's the rest operator. Same three characters. Context decides which one you're looking at.

Spread pushes values out. Rest pulls values in.

Spread: Expanding Values Out

Spread takes something that contains multiple values, an array or an object, and unpacks them into individual pieces. Think of it as opening a box and laying everything out on the table.

The most common use is combining arrays. You have two lists and you want one. Before spread, you'd reach for .concat() or a loop. With spread, it's a single line.

const fruits = ["apple", "mango"];
const veggies = ["carrot", "spinach"];

// Spread both arrays into a new one
const groceries = [...fruits, ...veggies];
// ["apple", "mango", "carrot", "spinach"]

// You can mix in extra items anywhere
const fullCart = ["bread", ...fruits, "milk"];
// ["bread", "apple", "mango", "milk"]

Spread also makes copying arrays safe. In JavaScript, if you do const b = a with an array, both b and a point to the same array in memory. Change one, you change the other. Spread creates an actual independent copy.

const original = [1, 2, 3];

// This is NOT a copy, both point to the same array
const stillSame = original;
stillSame.push(4);
console.log(original); // [1, 2, 3, 4] 

// Spread makes a real copy
const safeCopy = [...original];
safeCopy.push(99);
console.log(original); // [1, 2, 3] 

Spread With Objects

Spread works on objects too, and this is where it becomes genuinely useful in everyday code. The pattern shows up constantly when you want to update one property without touching the rest of an object.

const defaultSettings = {
  theme: "dark",
  fontSize: 14,
  language: "en"
};

const userPrefs = { fontSize: 18, language: "hi" };

// Merge: user prefs override defaults
const finalSettings = { ...defaultSettings, ...userPrefs };
// { theme: "dark", fontSize: 18, language: "hi" }

// Update one field without mutating the original
const updated = { ...defaultSettings, theme: "light" };
// { theme: "light", fontSize: 14, language: "en" }

When keys clash, the last one wins. So ...defaultSettings first, then ...userPrefs means user preferences override the defaults. Flip the order and defaults would overwrite users. Position matters.

Rest: Collecting Values In

Rest does the opposite. Instead of spreading values out, it gathers them together. You use it in two places: function parameters and destructuring assignments.

In function parameters, rest collects all the arguments you didn't name explicitly into one array. This is how you write a function that accepts any number of arguments.

// ...scores collects everything after the first argument
function printResults(name, ...scores) {
  console.log(`${name}'s scores:`, scores);
}

printResults("User1", 88, 92, 76);
// User1's scores: [88, 92, 76]

printResults("User2", 95);
// User2's scores: [95]

printResults("User3", 70, 85, 90, 88, 77);
// User3's scores: [70, 85, 90, 88, 77]

The rest parameter always has to be last. You can have as many named parameters before it as you want. But once you write ... in the parameter list, nothing else can come after it.

const [first, second, ...remaining] = [10, 20, 30, 40, 50];
console.log(first);     // 10
console.log(second);    // 20
console.log(remaining); // [30, 40, 50]

// Works with objects too
const { name, ...rest } = { name: "User1", age: 28, city: "Delhi" };
console.log(name); // "User1"
console.log(rest); // { age: 28, city: "Delhi" }

Spread vs Rest

The real question is: how do you tell which one you're looking at? The answer is where the ... appears.

If it's on the right side of an assignment, or inside a function call, or inside an array or object literal, it's spread. It's taking something and expanding it. If it's in a function's parameter definition, or on the left side of a destructuring assignment, it's rest. It's collecting things that haven't been named yet.

Spread (...) Rest (...)
Appears in function calls, not definitions Appears in function definitions, not calls
Appears inside [ ] or { } literals Appears on left side of destructuring
Takes one thing and expands it into many Takes many things and collects them into one
Works on arrays, objects, and strings Result is always a real array

Practical Use Cases

Passing an array as function arguments. Some functions expect individual arguments, not arrays.

const scores = [88, 76, 95, 63];

// Math.max doesn't take arrays, it takes individual numbers
Math.max(scores);         // NaN : won't work
Math.max(...scores);       // 95 : works

**
Adding an item to an array without mutating.**

const cart = ["shoes", "hat"];

// Add an item without touching the original
const newCart = [...cart, "jacket"];
// ["shoes", "hat", "jacket"]

// cart is still ["shoes", "hat"]

Writing utility functions that accept any number of arguments. Rest makes this clean without needing the old arguments object.

function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}

sum(1, 2);              // 3
sum(10, 20, 30);        // 60
sum(5);                // 5
sum();                  // 0

Stripping one key from an object. A common pattern when you need to pass an object somewhere but want to exclude a specific field, like removing a password before sending user data to the client.

// removing a key via destructuring + rest
const userData = {
  id: 1,
  name: "User1",
  password: "secret123",
  role: "admin"
};

// Pull out password, keep everything else
const { password, ...safeData } = userData;
// safeData = { id: 1, name: "User1", role: "admin" }
// password variable holds "secret123" but isn't passed anywhere

What You Get Out Of This

REFERENCES:

Simply JavaScript

Part 12 of 25

JavaScript is a quirky language. To master it, one should know to avoid its hidden traps along with its logic. This series showcase my journey through JS: the pain points, the breakthroughs, and the coding standards that I adopted from my mentors.

Up next

Async / Await

Sequential vs. Parallel Execution in Modern JS Applications