Rest and Spread in JavaScript

Rest and spread in JavaScript use the same three dots syntax, but they do two different jobs. Rest collects multiple values into a single array or object, while spread expands an array, object, or iterable into individual values. Many beginners see the same syntax and assume it always behaves the same way, but the meaning depends on where the operator appears in the code.

If you understand that one version gathers values and the other version expands values, the topic becomes much easier. Rest is common in function parameters and destructuring patterns. Spread is common when copying arrays, merging objects, passing arguments, or building a new value from an existing one without mutating the original data.

What rest and spread mean

The rest operator collects the remaining values. In a function, it gathers extra arguments into an array. In destructuring, it gathers the remaining properties or elements after specific ones have already been picked out. This makes it useful when the number of values is not fixed in advance.

The spread operator does the opposite. It expands an iterable such as an array or string, or expands the enumerable properties of an object, into a new place. That new place might be a function call, a new array literal, or a new object literal. The original value is not automatically modified unless you later change it directly.

PatternBehaviorTypical Use
`function sum(…nums)`Rest collects many arguments into one arrayVariable length function parameters
`const copy = […arr]`Spread expands array elements into a new arrayCopying or combining arrays
`const clone = {…obj}`Spread expands object properties into a new objectCloning or merging objects
`const [first, …rest] = arr`Rest collects remaining array itemsArray destructuring
`const {id, …others} = user`Rest collects remaining object propertiesObject destructuring

Rest parameters in functions

Before rest parameters, JavaScript developers often used the special `arguments` object. That still exists in regular functions, but it is array-like rather than a real array, and it is not available in arrow functions the same way. Rest parameters are cleaner because they produce a real array that works naturally with methods like `map`, `filter`, and `reduce`.

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

console.log(sum(2, 4, 6));
console.log(sum(1, 3, 5, 7, 9));

In this example, `…numbers` collects however many extra arguments are passed into the function and stores them in the `numbers` array. That means the same function can handle two values, five values, or none at all. This is one of the most practical uses of rest in everyday code.

One important rule is that the rest parameter must be the last parameter in the function signature. JavaScript needs to know where normal parameters stop and where the remaining values begin. If you try to place another parameter after a rest parameter, the code is invalid.

function createUser(role, ...skills) {
  return { role, skills };
}

console.log(createUser("developer", "JavaScript", "Node.js", "SQL"));

Rest in destructuring

Rest also works in destructuring assignments. When you extract a few values and want everything else grouped together, rest becomes a compact solution. This pattern is useful when processing API responses, configuration objects, or lists where only part of the data needs direct access.

const colors = ["red", "green", "blue", "black"];
const [primary, secondary, ...others] = colors;

console.log(primary);
console.log(secondary);
console.log(others);

const user = {
  id: 101,
  name: "Ava",
  role: "admin",
  active: true
};

const { id, ...profile } = user;
console.log(id);
console.log(profile);

In array destructuring, rest captures the remaining elements into a new array. In object destructuring, it collects the remaining properties into a new object. This is especially useful when you want to remove one property, such as an identifier or internal flag, while keeping the rest of the data together.

Spread with arrays

Spread is often used with arrays because it can expand one array into another. This is cleaner than writing loops just to copy or combine values. It also encourages an immutable style where you create new arrays instead of changing existing ones in place.

const frontend = ["HTML", "CSS"];
const backend = ["Node.js", "MongoDB"];

const fullStack = [...frontend, "JavaScript", ...backend];
console.log(fullStack);

const original = [1, 2, 3];
const copy = [...original];
console.log(copy);

The copied array is a new array, so pushing a new value into `copy` will not push it into `original`. However, this is still a shallow copy. If the array contains nested objects or arrays, those nested references are shared unless you copy them separately.

Spread with objects

Object spread is commonly used to clone objects or merge several objects into one. When properties overlap, the later property wins because it is applied last. This behavior is convenient for overriding defaults with user supplied values.

const defaults = {
  theme: "light",
  sidebar: true,
  language: "en"
};

const userSettings = {
  theme: "dark"
};

const finalSettings = { ...defaults, ...userSettings };
console.log(finalSettings);

This pattern appears everywhere in modern JavaScript, especially in UI code, state updates, and configuration building. It keeps the original object untouched while producing a new object that reflects the latest values.

Spread in function calls

Spread can also expand array values into separate function arguments. This is helpful when a function expects individual arguments, but your data is already stored in an array. Instead of manually indexing every element, spread performs the expansion directly.

const numbers = [12, 45, 8];
console.log(Math.max(...numbers));

const dateParts = [2026, 4, 12];
const eventDate = new Date(...dateParts);
console.log(eventDate);

Common mistakes and limits

The first common mistake is confusing rest and spread just because both use `…`. Read the code position carefully. If the syntax appears in a parameter list or on the left side of destructuring, it is usually gathering values as rest. If it appears inside a new array, object, or function call, it is usually expanding values as spread.

The second mistake is assuming spread performs a deep copy. It does not. Spread copies one level. Nested objects still refer to the same inner data. If you need a true deep copy, you need a different strategy that matches the shape of the data, such as structured cloning or manual copying of nested layers.

  • Use rest when the number of incoming values is not fixed.
  • Use spread when building a new array or object from existing data.
  • Remember that rest parameters must appear last in the parameter list.
  • Treat spread copies as shallow copies, not full deep clones.

Practical use cases

Rest is useful for utility functions, logging functions, and wrappers that pass through a variable number of arguments. Spread is useful for state updates, merging settings, copying arrays before sorting, and composing argument lists. In modern JavaScript, both features support cleaner and more predictable code because they reduce manual loops and repetitive glue code.

function logMessages(level, ...messages) {
  console.log(level, messages.join(" | "));
}

const baseUser = { name: "Liam", role: "editor" };
const promotedUser = { ...baseUser, role: "admin", active: true };

logMessages("INFO", "server started", "cache warm", "ready");
console.log(promotedUser);

FAQ

Are rest and spread different features or the same feature?

They are related syntax forms that use the same three dots, but their behavior depends on context. Rest collects values, while spread expands values.

Can spread copy nested objects safely?

Spread only makes a shallow copy. Nested objects or arrays remain shared references unless you copy those inner values separately.

Why are rest parameters preferred over arguments?

Rest parameters give you a real array, clearer intent, and better compatibility with modern JavaScript patterns such as arrow functions and array methods.

Rest, spread, and immutable updates

Rest and spread are heavily used in modern front end code because they support immutable updates. Instead of editing the original array or object directly, you build a new value that contains the old data plus the changes you want. This pattern is common in state management because it makes updates easier to track and reason about.

const cart = ["keyboard", "mouse"];
const updatedCart = [...cart, "monitor"];

const profile = { name: "Mira", plan: "basic" };
const upgradedProfile = { ...profile, plan: "pro" };

console.log(updatedCart);
console.log(upgradedProfile);

This style reduces accidental mutation and fits well with component based libraries where change detection often depends on new references. At the same time, you should still remember that spread only copies one level. If an object contains nested structures, the inner references are still shared unless you copy them deliberately.

arguments object vs rest parameters

Many older tutorials use the `arguments` object to handle unknown numbers of function arguments. Rest parameters are usually better because they are explicit and return a true array. That means modern code can work with array methods immediately, without converting anything first.

function oldStyle() {
  console.log(arguments.length);
}

function modernStyle(...values) {
  console.log(values.length);
  console.log(values.map(value => value.toUpperCase()));
}

oldStyle("a", "b", "c");
modernStyle("red", "green", "blue");

If you are maintaining legacy code, you will still see `arguments`, but for new code rest parameters communicate intent better. They show clearly that the function is designed to accept a flexible number of values.

When spread should be used carefully

Spread is convenient, but convenience should not replace judgment. Repeatedly copying very large arrays or objects inside tight loops can add overhead. Merging objects with duplicate property names can also hide bugs if you forget that later properties overwrite earlier ones. The syntax is simple, but the outcome still depends on the data shape and the order of operations.

Choosing between rest and spread quickly

If you need a simple memory rule, ask whether the code is collecting or expanding. A function that receives many values and groups them into one array is using rest. An array or object that is being unpacked into another place is using spread. This question is usually enough to identify the correct meaning in real code.

In practice, rest helps define flexible APIs, while spread helps create clean updates and combinations of existing data. Once that mental model becomes natural, the three dots stop feeling confusing and start feeling like one of the most practical pieces of modern JavaScript syntax.