# Spread vs Rest Operators in JavaScript


The spread and rest operators look identical: three dots (`...`). But they do opposite things. One expands values. One collects them. Understand the difference and you'll write cleaner, more powerful code.

This is about what spread and rest do, how they differ, and when to use each one.

* * *

## Understanding the Three Dots

The `...` syntax appears in two different contexts:

```plaintext
Spread:  Takes many elements and spreads them out
Rest:    Takes many values and collects them together
```

The syntax is the same. The context is different. That's what matters.

* * *

## What the Spread Operator Does

The spread operator takes an iterable (array, string, or object) and expands it in-place.

### Expanding an Array

```javascript
const arr = [1, 2, 3];

console.log(arr);        // [1, 2, 3]
console.log(...arr);     // 1 2 3
```

Without spread: The array is printed as an array.

With spread: The elements are printed individually.

### Spread Unpacks Values

```plaintext
Without spread:  [1, 2, 3]
                 One array

With spread:     1, 2, 3
                 Three separate values
```

### Real Example: Function Arguments

```javascript
function add(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];

// Without spread
add(numbers[0], numbers[1], numbers[2]);  // 6 (clunky)

// With spread
add(...numbers);  // 6 (clean)
```

Spread takes `[1, 2, 3]` and expands it to `1, 2, 3`. Then those values are passed as separate arguments.

### Spread with Math Functions

```javascript
const numbers = [5, 6, 2, 8, 1];

// Find max without spread
Math.max(numbers[0], numbers[1], numbers[2], numbers[3], numbers[4]); // 8

// With spread
Math.max(...numbers); // 8
```

`Math.max()` expects individual arguments, not an array. Spread converts the array to individual arguments.

### Spreading Strings

Strings are iterable. You can spread them:

```javascript
const str = "hello";

console.log(...str);  // h e l l o

const arr = [...str];
console.log(arr);     // ["h", "e", "l", "l", "o"]
```

Spread expands the string into individual characters.

* * *

## What the Rest Operator Does

The rest operator collects multiple values into a single array.

### Collecting Function Arguments

```javascript
function sum(...numbers) {
  console.log(numbers);  // An array of all arguments
  let total = 0;
  for (let num of numbers) {
    total += num;
  }
  return total;
}

sum(1, 2, 3);        // numbers = [1, 2, 3]
sum(1, 2, 3, 4, 5);  // numbers = [1, 2, 3, 4, 5]
```

Rest collects all arguments into a single array `numbers`.

### Rest Collects Values

```plaintext
Without rest:  function (a, b, c) { }
               Can only accept 3 arguments

With rest:     function (...args) { }
               Can accept any number of arguments
```

### Rest with Regular Parameters

```javascript
function greet(greeting, ...names) {
  console.log(greeting);  // Single value
  console.log(names);     // Array of remaining values
}

greet("Hello", "Alice", "Bob", "Charlie");

// Output:
// "Hello"
// ["Alice", "Bob", "Charlie"]
```

The first argument goes to `greeting`. The rest go into the `names` array.

### Rest in Array Destructuring

```javascript
const arr = [1, 2, 3, 4, 5];

const [first, second, ...rest] = arr;

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

The first two elements are assigned individually. The rest are collected into an array.

### Rest in Object Destructuring

```javascript
const person = {
  name: "John",
  age: 30,
  city: "New York",
  country: "USA"
};

const { name, age, ...location } = person;

console.log(name);      // "John"
console.log(age);       // 30
console.log(location);  // { city: "New York", country: "USA" }
```

Named properties are extracted. The rest are collected into a new object.

* * *

## Spread vs Rest: Key Differences

### Context Matters

| Usage | Operator | Name | Does |
| --- | --- | --- | --- |
| Function call | `...arr` | Spread | Expands array to arguments |
| Array literal | `[...arr]` | Spread | Expands array into new array |
| Function parameter | `(...args)` | Rest | Collects arguments into array |
| Destructuring | `[a, ...rest]` | Rest | Collects remaining into array |

### Visual Comparison

```plaintext
SPREAD (Unpacking)
[1, 2, 3]
   ↓
1, 2, 3

REST (Collecting)
1, 2, 3
   ↓
[1, 2, 3]
```

Spread goes outward. Rest goes inward.

### Position Matters

**Spread can appear anywhere in an expression:**

```javascript
const a = [1, 2];
const b = [3, 4];

const combined = [0, ...a, ...b, 5];
console.log(combined);  // [0, 1, 2, 3, 4, 5]
```

**Rest can only be the last parameter:**

```javascript
function test(...rest, param) { }  // ERROR
function test(param, ...rest) { }  // CORRECT
```

Rest must come last because it collects everything remaining.

* * *

## Using Spread with Arrays

### Copy an Array

```javascript
const original = [1, 2, 3];

// Shallow copy
const copy = [...original];

console.log(copy);           // [1, 2, 3]
console.log(copy === original); // false (different arrays)
```

Spread creates a new array with the same elements.

### Combine Arrays

```javascript
const arr1 = [1, 2];
const arr2 = [3, 4];

const combined = [...arr1, ...arr2];
console.log(combined);  // [1, 2, 3, 4]
```

Spread expands both arrays into a new one.

### Insert Elements

```javascript
const arr = [1, 2, 5];

// Insert 3 and 4 between 2 and 5
const result = [1, 2, 3, 4, 5];

// Or do it with spread
const start = [1, 2];
const end = [5];
const final = [...start, 3, 4, ...end];
console.log(final);  // [1, 2, 3, 4, 5]
```

### Remove Duplicates

```javascript
const arr = [1, 2, 2, 3, 3, 3, 4];

// Using Set (removes duplicates)
const unique = [...new Set(arr)];
console.log(unique);  // [1, 2, 3, 4]
```

Spread converts the Set back to an array.

### Reverse an Array

```javascript
const arr = [1, 2, 3];

// Using spread with reverse
const reversed = [...arr].reverse();
console.log(arr);        // [1, 2, 3] (original unchanged)
console.log(reversed);   // [3, 2, 1] (new reversed array)
```

Spread creates a copy first, so the original isn't modified.

* * *

## Using Spread with Objects

### Copy an Object

```javascript
const original = { name: "John", age: 30 };

// Shallow copy
const copy = { ...original };

console.log(copy);              // { name: "John", age: 30 }
console.log(copy === original); // false (different objects)
```

Spread creates a new object with the same properties.

### Merge Objects

```javascript
const obj1 = { name: "John", age: 30 };
const obj2 = { city: "New York", country: "USA" };

const merged = { ...obj1, ...obj2 };
console.log(merged);
// { name: "John", age: 30, city: "New York", country: "USA" }
```

Spread expands both objects into one.

### Override Properties

```javascript
const defaults = { theme: "light", language: "en" };
const userSettings = { theme: "dark" };

const settings = { ...defaults, ...userSettings };
console.log(settings);
// { theme: "dark", language: "en" }
```

Properties from `userSettings` override defaults because they come later.

### Add New Properties

```javascript
const user = { name: "John", age: 30 };

const updated = {
  ...user,
  age: 31,
  email: "john@example.com"
};

console.log(updated);
// { name: "John", age: 31, email: "john@example.com" }
```

Spread the original, then add or override properties.

### Exclude Properties

```javascript
const user = { name: "John", age: 30, password: "secret" };

// Remove password
const { password, ...publicUser } = user;

console.log(publicUser);
// { name: "John", age: 30 }
```

Rest collects everything except the excluded property.

* * *

## Practical Use Cases

### Use Case 1: Flexible Function Arguments

```javascript
// Without rest - limited to exact arguments
function oldSum(a, b, c) {
  return a + b + c;
}

oldSum(1, 2, 3);       // 6
oldSum(1, 2, 3, 4);    // 10 (4 ignored)

// With rest - any number of arguments
function newSum(...numbers) {
  return numbers.reduce((sum, n) => sum + n, 0);
}

newSum(1, 2, 3);       // 6
newSum(1, 2, 3, 4);    // 10
newSum(1, 2, 3, 4, 5); // 15
```

### Use Case 2: Cloning Data

```javascript
const original = { id: 1, name: "Product", price: 99.99 };

// Create a copy to modify
const edited = { ...original, price: 79.99 };

console.log(original); // { id: 1, name: "Product", price: 99.99 }
console.log(edited);   // { id: 1, name: "Product", price: 79.99 }
```

The original remains unchanged.

### Use Case 3: Passing Arguments

```javascript
function greet(greeting, name, punctuation) {
  return greeting + " " + name + punctuation;
}

const args = ["Hello", "Alice", "!"];

// Without spread
greet(args[0], args[1], args[2]);  // "Hello Alice!"

// With spread
greet(...args);  // "Hello Alice!"
```

Spread converts the array to function arguments.

### Use Case 4: State Management (React-style)

```javascript
// Update a single property in state
const state = { user: "John", count: 5, loading: false };

const newState = {
  ...state,
  count: state.count + 1
};

console.log(state);    // { user: "John", count: 5, loading: false }
console.log(newState); // { user: "John", count: 6, loading: false }
```

Create a new object with one property changed.

### Use Case 5: API Response Handling

```javascript
const apiResponse = {
  status: 200,
  data: { id: 1, name: "John" },
  timestamp: "2026-05-04"
};

// Extract just the data
const { status, timestamp, ...userData } = apiResponse;

console.log(userData);  // { id: 1, name: "John" }
```

Rest extracts the important data.

### Use Case 6: Building Arrays Dynamically

```javascript
const baseItems = ["apple", "banana"];
const newItems = ["orange"];
const userItems = ["grape"];

const groceryList = [
  "store items:",
  ...baseItems,
  ...newItems,
  ...userItems
];

console.log(groceryList);
// ["store items:", "apple", "banana", "orange", "grape"]
```

Spread combines multiple arrays cleanly.

### Use Case 7: Default Parameters with Objects

```javascript
const defaultConfig = {
  timeout: 5000,
  retries: 3,
  verbose: false
};

function makeRequest(url, config = {}) {
  const finalConfig = { ...defaultConfig, ...config };
  return finalConfig;
}

makeRequest("https://api.example.com");
// { timeout: 5000, retries: 3, verbose: false }

makeRequest("https://api.example.com", { timeout: 10000 });
// { timeout: 10000, retries: 3, verbose: false }
```

Merge user config with defaults.

### Use Case 8: Filtering with Rest

```javascript
const numbers = [1, 2, 3, 4, 5];

const [first, ...rest] = numbers;
const even = rest.filter(n => n % 2 === 0);

console.log(even);  // [2, 4]
```

Extract and process remaining elements.

* * *

## Spread and Rest Diagrams

### Spread: Expanding Elements

```plaintext
Array: [1, 2, 3]
    ↓
Spread: ...arr
    ↓
Expanded: 1, 2, 3
    ↓
Used in: Function calls, array literals, object literals
```

### Rest: Collecting Values

```plaintext
Values: 1, 2, 3, 4, 5
    ↓
Rest: ...rest
    ↓
Collected: [1, 2, 3, 4, 5]
    ↓
Stored in: Array parameter, destructuring
```

### Visual Comparison

```plaintext
SPREAD (Unpacking)
        [1, 2, 3]
              ↓
        1 , 2 , 3
              ↓
    Pass to function / merge

REST (Collecting)
        1 , 2 , 3
              ↓
        [1, 2, 3]
              ↓
    Store in variable
```

* * *

## Advanced Examples

### Combining Spread and Rest

```javascript
const arr = [1, 2, 3, 4, 5];

// Destructure with rest
const [first, second, ...remaining] = arr;

// Spread to reconstruct
const reconstructed = [first, second, ...remaining];

console.log(reconstructed);  // [1, 2, 3, 4, 5]
```

### Deep Object Merging (Single Level)

```javascript
const user = { name: "John", address: { city: "NY" } };
const update = { address: { zip: "10001" } };

// Shallow merge - overwrites entire address
const result = { ...user, ...update };
console.log(result);
// { name: "John", address: { zip: "10001" } }
// Note: city is lost!

// For deep merge, handle nested objects
const deepResult = {
  ...user,
  address: { ...user.address, ...update.address }
};
console.log(deepResult);
// { name: "John", address: { city: "NY", zip: "10001" } }
```

### Rest with Named Parameters

```javascript
function createUser(firstName, lastName, ...metadata) {
  return {
    name: firstName + " " + lastName,
    meta: metadata
  };
}

const user = createUser(
  "John",
  "Doe",
  { role: "admin" },
  { permissions: ["read", "write"] }
);

console.log(user);
// {
//   name: "John Doe",
//   meta: [{ role: "admin" }, { permissions: ["read", "write"] }]
// }
```

### Spread in Conditional Logic

```javascript
const features = ["auth", "api"];
const allFeatures = ["auth", "api", "admin", "logging"];

// Check if all features are included
const hasAllFeatures = features.every(f => allFeatures.includes(f));

// Or, spread and use includes
const isSubset = [...features].every(f => allFeatures.includes(f));
```

* * *

## Common Mistakes

### Mistake 1: Using Rest in Wrong Position

```javascript
// WRONG - rest must be last
function test(a, ...rest, b) { }

// RIGHT
function test(a, b, ...rest) { }
```

Rest collects all remaining values. It must be last.

### Mistake 2: Confusing Spread and Rest by Symbol

```javascript
// WRONG - looks the same but different meaning
const arr = [...[1, 2, 3]];  // Spread - unpacking
const fn = (...args) => {};  // Rest - collecting

// RIGHT - understand the context
// In array/call: spread
// In parameters/destructuring: rest
```

### Mistake 3: Deep Copy with Spread

```javascript
const original = { user: { name: "John" } };

// WRONG - only shallow copy
const copy = { ...original };
copy.user.name = "Jane";

console.log(original.user.name);  // "Jane" (changed!)

// RIGHT - for nested objects, use deep copy or spread nested objects
const deepCopy = {
  ...original,
  user: { ...original.user }
};
```

Spread only does shallow copying.

### Mistake 4: Spreading Non-Iterables

```javascript
// WRONG - numbers are not iterable
const num = 123;
const arr = [...num];  // TypeError

// RIGHT - only spread iterables (arrays, strings, objects with Symbol.iterator)
const str = "hello";
const arr = [...str];  // ["h", "e", "l", "l", "o"]
```

### Mistake 5: Rest in Middle of Destructuring

```javascript
// WRONG - rest must be last
const { a, ...rest, b } = obj;

// RIGHT
const { a, b, ...rest } = obj;
```

* * *

## Quick Recap

- **Spread operator (`...`)** in array/object literals or function calls: Expands elements.
  - `console.log(...[1,2,3])` → prints `1 2 3`
  - `Math.max(...[5,6,2])` → returns `6`
  - `{...obj}` → creates a copy

- **Rest operator (`...`)** in function parameters or destructuring: Collects values into an array.
  - `function(...args)` → collects all arguments
  - `const [a, ...rest] = arr` → collects remaining elements
  - `const { name, ...rest } = obj` → collects remaining properties

- **Context determines meaning:**
  - In arrays/calls: Spread (unpacking)
  - In parameters/destructuring: Rest (collecting)

- **Spread uses:**
  - Copy arrays and objects
  - Merge arrays and objects
  - Pass array elements as function arguments
  - Insert elements into arrays

- **Rest uses:**
  - Accept variable number of arguments
  - Destructure arrays and objects
  - Extract specific values and collect the rest

- **Spread is shallow** - nested objects/arrays aren't fully copied.

- **Rest must be last** - it collects all remaining values.

Master spread and rest. Write cleaner, more flexible code. You'll use them constantly.

Happy coding! 🚀

* * *

> If you enjoyed this article, check out my other blogs on this profile. 🔗 Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)
