Demystifying ... (Spread and Rest) in JavaScript

himanshuhimanshu
9 min read

ES6 is an exciting update to JavaScript that provides many powerful features, such as Promises, the const keyword, and destructuring of arrays and objects, among others. One of the powerful features of the ES6 update is the spread and rest operators. These two operators are powerful, but they are also often misunderstood.

The syntax for both operators is the same (…) but they work differently depending on the context. Let's explore these operators to clear up any confusion.

Spread Operator (…)

According to MDN:

The spread (...) syntax allows an iterable, such as an array or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. In an object literal, the spread syntax enumerates the properties of an object and adds the key-value pairs to the object being created.

This means you can spread an array into its individual elements. Let’s take an example,

// syntax 
const num1 = [1,2,3,4,5];
const num2 = [...num1];

console.log("num1 - ", num1); // num1 - [1,2,3,4,5]
console.log("num2 - ", num2); // num2 - [1,2,3,4,5]

Here, we created an array num1 and used the spread operator ([...num1]) to create a copy of num1 and assign it to a variable num2.

Let’s take another example to understand the use of spread operator in functions

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// Expected output: 6

console.log(sum.apply(null, numbers));
// Expected output: 6

Only iterable values, like Array and String, can be spread in array literals and argument lists. Many objects are not iterable, including all plain objects that lack a Symbol.iterator method:

const obj = { key1: "value1" };
const array = [...obj]; // TypeError: obj is not iterable

Use Cases

  1. Copying Arrays

Creating a shallow copy of an array:

// syntax 
const num1 = [1,2,3,4,5];
const num2 = [...num1];

console.log("num1 - ", num1); // num1 - [1,2,3,4,5]
console.log("num2 - ", num2); // num2 - [1,2,3,4,5]

Here, [...num1] creates a new array by spreading the elements of num1. This is safer than directly assigning with = (which copies the reference, not the data).

  1. Merging Arrays

You can merge two or more arrays cleanly:

const num1 = [1,2,3,4,5];
const num2 = [...num1];

const num3 = [...num1 , ...num2];

console.log(num3); //Expected Output : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
  1. Spreading elements into function arguments

Sometimes you have an array of arguments you want to pass into a function:

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// Expected output: 6

The spread operator breaks the array into individual arguments for the function.

  1. Cloning Objects and Merging Objects

Since ES2018, you can also use spread with objects:

const user = { name: "Alice", age: 25 };
const updatedUser = { ...user, age: 26 };
console.log(updatedUser); // { name: "Alice", age: 26 }

Here, we copied the user object and updated the age key in a clean, immutable way.

  1. Spreading Strings

The spread operator can break a string into characters:

const name = "Code";
const letters = [...name];
console.log(letters); // ['C', 'o', 'd', 'e']

This is especially useful when converting strings into arrays of characters.

Important Note

  • Spread operator only works on iterables like arrays, strings, maps, and sets.

  • It performs only Shallow copy — meaning nested structures (array/objects inside) are still referenced.

Rest Operator (…)

According to MDN:

The rest parameter syntax allows a function to accept an indefinite number of arguments as an array, providing a way to represent variadic functions in JavaScript.

You can use the rest operator in:

  • Function parameters

  • Array destructuring

  • Object destructuring

It allows developers to write flexible, cleaner, and more dynamic code by handling variable numbers of values.

🧠 Think of it like:

“Collecting the rest of the elements into a group.”

Use Cases

  1. Rest in Function arguments

When a function can accept any number of arguments, rest helps collect them into an array.

function sum(...theArgs) {
  let total = 0;
  for (const arg of theArgs) {
    total += arg;
  }
  return total;
}

console.log(sum(1, 2, 3));
// Expected output: 6

console.log(sum(1, 2, 3, 4));
// Expected output: 10
  1. Rest in Array Destructuring

You can collect the remaining elements of an array into another array.

const num = [1,2,3,4,5];
const [first, second, ...others] = num;

console.log(first); // 1
console.log(second); // 2
console.log(others); // [3,4,5]

This is especially useful when you want to extract only the first few items and group the rest.

  1. Rest in Object Destructuring

Rest can also collect the remaining properties of an object:

const {name, ...otherDetail} = {
  name: "Alice",
  age: 34,
  city: "faridabad"
}

console.log(name); // Alice
console.log(otherDetail); // {age: 34, city: "faridabad"}

This is often used when separating required props from optional or extra props in libraries like React.

⚠️ Rules and Limitations

  • In function parameters, only the last parameter can be a rest parameter.

      function example(...args, last) {} // ❌ SyntaxError
    
  • Rest works only in:

    • Function definitions

    • Destructuring assignments (arrays/objects)

  • Rest always returns an array (or object), never single values.

Key Differences Between Spread and Rest

FeaturesSpread OperatorRest Operator
PurposeExpandGathers
Use inArrays, Objects, function callsFunction params, destructuring
Data typeIterableAny number of arguments / elements

Common Mistakes

Even though both the spread and rest operators use the same syntax (...), they behave very differently depending on where and how they are used. This often leads to confusion, especially for those new to JavaScript or ES6.

Let’s look at the most frequent mistakes developers make, and how to avoid them:

❌ 1. Confusing Spread and Rest Due to Same Syntax

Mistake:
Many developers think ... always does the same thing.

function test(...args) {
  console.log(...args); // ❌ Incorrect if misunderstood
}

Why it’s wrong:

  • ...args in the parameter is rest (it collects).

  • ...args in the console log is spread (it expands).

🧠 Tip:

Use rest when you're receiving multiple values.
Use spread when you're passing multiple values.


❌ 2. Using Spread on Non-Iterable Values

const obj = { a: 1, b: 2 };
const copy = [...obj]; // ❌ TypeError: obj is not iterable

Why it’s wrong:
Spread works with iterables like arrays, strings, maps, and sets—not plain objects in array syntax.

✅ Correct way to spread an object:

const copy = { ...obj }; // ✅ Valid with object spread syntax

❌ 3. Expecting a Deep Copy with Spread

const original = { nested: { value: 10 } };
const copy = { ...original };

copy.nested.value = 99;
console.log(original.nested.value); // 😬 99

Why it’s wrong:
Spread creates a shallow copy—it only copies the top level. Nested objects remain linked by reference.

✅ Solution: Use deep cloning with structuredClone() or libraries like Lodash (cloneDeep()).


❌ 4. Using Rest in the Wrong Position in Function Parameters

function wrong(...args, last) {} // ❌ SyntaxError

Why it’s wrong:
Rest must be the last parameter in a function. JavaScript wouldn’t know how many arguments to assign to last.

✅ Correct:

function correct(first, ...rest) {}

❌ 5. Trying to Use Rest Outside of a Valid Context

const result = ...[1, 2, 3]; // ❌ SyntaxError

Why it’s wrong:
Rest can only be used:

  • Inside function parameters

  • During destructuring of arrays or objects

Real-World Use Cases of Spread and Rest Operators

The spread and rest operators aren’t just abstract syntax features — they’re used all the time in modern JavaScript applications, especially in frameworks like React, Node.js, and in data manipulation tasks.

Let’s explore how they are applied in real-world scenarios.

  1. React Props Handling (Rest + Spread)

In React, the combination of rest and spread is frequently used to manage props.

function Button({label, ...restProps}) {
    return <button {...restProps}>{label}</button>
}
  • Rest is used to gather all props except label.

  • Spread is used to pass all remaining props (like onClick, className, etc) to the <button>.

This makes your components clean, flexible, and DRY (Don’t Repeat Yourself).

  1. Immutable State Updates

When updating arrays or objects immutably — like in Redux or React’s useState — you are spread to create a copy and apply changes.

const oldUser = { name: "Alice", age: 25 };
const updatedUser = { ...oldUser, age: 26 };
  • This doesn’t mutate the original object.

  • Essential for predictable state management in UI libraries.

  1. Merging Configurations or Defaults

Combining default settings with user-provided options using object spread:

const defaultConfig = { theme: "light", debug: false };
const userConfig = { debug: true };

const finalConfig = { ...defaultConfig, ...userConfig };
// Result : { theme: "light", debug: true }

The second spread (userConfig) overrides matching properties in defaultConfig.

  1. Handling Variable Number of Arguments

When you want a function to accept any number of inputs:

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

sumAll(1,2,3,4); // 10

The rest parameter gathers all incoming values into an array for processing.

  1. Array Flattening or Expansion

const nums = [1,2,3];
const moreNums = [0, ...nums, 4];
console.log(moreNums) // [0,1,2,3,4]

Spread is used to expand nums into the new array, avoiding concat().

  1. Removing a Property from an Object (Destructuring + Rest)

const {password, ...userWithoutPassword} = {
    name: "Bob",
    password: "secret",
    email: "bob@gmail.com"
};

console.log(userWithoutPassword);
// { name: "Bob", email: "bob@gmail.com" }

This is useful for privacy (like sending safe user data to the frontend).

Conclusion

The spread and rest operators (...) are some of the most powerful and elegant features introduced in modern JavaScript. Despite using the same three dots, they serve opposite purposes:

  • Spread is used to expand or unpack data.

  • Rest is used to gather or pack data.

Both offer:

  • Cleaner syntax

  • More readable code

  • Flexibility in handling arrays, objects, and function arguments

From copying and merging objects, to filtering props in React components or building flexible APIs, mastering these operators can significantly level up your JavaScript coding skills.

✨ Final Tip:

Always remember:

  • Use spread when you want to break things apart

  • Use rest when you want to put things together

📘 What to Do Next?

  • Try rewriting functions using rest/spread instead of arguments, concat, or Object.assign.

  • Explore real-world GitHub projects or React apps to see them in action.

  • Practice combining rest and spread to write cleaner, more expressive code.

0
Subscribe to my newsletter

Read articles from himanshu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

himanshu
himanshu