JavaScript Rest & Spread (...) Explained with Examples

Rest and spread syntax use the same three-dot form in JavaScript, but they solve opposite problems. Rest collects values into a single array or object, while spread expands an array, object, or function arguments into individual items.

Quick answer: Use ... as rest when you want to gather values, and as spread when you want to unpack values. In functions, arrays, objects, and destructuring, the meaning depends on where the ... appears.

Difficulty: Beginner

You'll understand this better if you know: arrays, function parameters, objects, and basic destructuring in JavaScript.

1. What Is JavaScript Rest & Spread (...)?

Rest and spread are two related uses of the same syntax token: the three-dot .... The meaning changes based on context.

Because the same symbol does different jobs, beginners often confuse rest with spread. A good rule is: if the dots appear on the left side of an assignment pattern or parameter list, they usually collect values; if they appear inside an array, object, or function call, they usually expand values.

2. Why JavaScript Rest & Spread Matters

Rest and spread make JavaScript code easier to read and maintain. Instead of writing manual loops, slicing arrays, or building new objects step by step, you can express your intent directly.

They matter because they let you:

They are especially useful in modern JavaScript codebases because they reduce boilerplate and make data transformation easier to follow.

3. Basic Syntax or Core Idea

Rest in function parameters

Use rest parameters when a function should accept any number of arguments. The rest parameter must be the last parameter in the list.

function sum(...numbers) {
  let total = 0;

  for (const number of numbers) {
    total += number;
  }

  return total;
}

Here, ...numbers collects all arguments into one array named numbers.

Spread in function calls

Use spread when you want to pass items from an array as separate arguments.

const values = [10, 20, 30];

Math.max(...values);

Here, ...values expands the array into separate arguments: Math.max(10, 20, 30).

Rest in destructuring

Use rest in destructuring to collect leftover values after matching the ones you want.

const [first, second, ...remaining] = ["a", "b", "c", "d"];

In this example, first and second get the first two values, and remaining gets the rest.

Spread in array and object literals

Use spread to create a new array or object that includes existing values.

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

const user = { name: "Ava" };
const updatedUser = { ...user, role: "admin" };

This creates new values instead of changing the original array or object.

4. Step-by-Step Examples

Example 1: Sum any number of arguments

This function uses rest parameters so callers do not need to know how many numbers are expected.

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

sum(5, 10, 15);

The function receives three arguments, but rest puts them into one array so reduce can process them.

Example 2: Find the largest value in an array

Spread is useful when a function expects separate arguments, but your data is already in an array.

const scores = [72, 88, 91, 85];
const highest = Math.max(...scores);

Without spread, you would have to list each score manually. With spread, the array becomes individual arguments automatically.

Example 3: Copy and extend an array

Spread is a common way to make a new array from an existing one.

const baseTags = ["javascript", "functions"];
const allTags = [...baseTags, "syntax", "rest"];

The new array contains all original items plus two more. The original array stays unchanged.

Example 4: Pick values with object rest

Object rest is useful when you want one or two properties and want the rest grouped together.

const profile = {
  id: 42,
  name: "Mina",
  role: "editor",
  active: true
};

const { id, ...details } = profile;

After destructuring, id contains one value, and details contains the remaining properties.

5. Practical Use Cases

These patterns are common in everyday JavaScript code, especially when working with lists, configuration, and data shaping.

6. Common Mistakes

Mistake 1: Using rest anywhere in a parameter list

Rest parameters must come last. If you place another parameter after them, JavaScript cannot know where the collected arguments should stop.

Problem: This function signature is invalid because the rest parameter is not the final parameter.

function joinNames(...names, separator) {
  return names.join(separator);
}

Fix: Put required parameters first, then the rest parameter at the end.

function joinNames(separator, ...names) {
  return names.join(separator);
}

The corrected version works because JavaScript can read the fixed parameter first and then collect the remaining arguments.

Mistake 2: Thinking spread mutates the original array or object

Spread creates a new array or object. It does not modify the original value, so changing the copy does not update the source.

Problem: Developers sometimes expect the original data to change when they edit the copied value.

const original = [1, 2];
const copy = [...original];

copy.push(3);

Fix: If you want a new value, use spread and then work with the new array or object explicitly.

const original = [1, 2];
const updated = [...original, 3];

The corrected version is predictable because it clearly creates a separate value instead of changing the original.

Mistake 3: Spreading a value that is not iterable

Array spread requires an iterable, such as an array, string, set, or map. If you try to spread a plain object into an array, JavaScript throws a runtime error such as TypeError: object is not iterable or is not iterable.

Problem: Plain objects cannot be expanded inside an array literal because they do not implement the iterable protocol.

const user = { name: "Kai", age: 29 };
const items = [...user];

Fix: Spread arrays into arrays, and spread objects into objects.

const user = { name: "Kai", age: 29 };
const copy = { ...user };

The corrected version works because object spread copies properties into another object instead of trying to build an array.

7. Best Practices

Prefer rest for flexible function APIs

When a function accepts an unknown number of values, rest parameters make the API simpler and easier to call.

function average(...numbers) {
  if (numbers.length === 0) {
    return 0;
  }

  const total = numbers.reduce((sum, number) => sum + number, 0);
  return total / numbers.length;
}

This keeps the function easy to extend without changing every caller.

Use spread to create new data instead of mutating old data

Spread is a clean way to build updated arrays and objects while preserving the original value.

const settings = { theme: "light", language: "en" };
const nextSettings = { ...settings, theme: "dark" };

This pattern is especially useful when you want predictable state updates.

Be careful with shallow copies

Spread copies only the top level. Nested arrays and objects are still shared references unless you copy them separately.

const original = {
  name: "Rin",
  profile: { city: "Tokyo" }
};

const copy = { ...original };
copy.profile.city = "Osaka";

Because profile is still shared, this change affects both objects. If you need deep cloning, spread alone is not enough.

8. Limitations and Edge Cases

Note: If you need to copy a class instance, preserve methods, or duplicate nested data deeply, you need a different approach than spread.

9. Practical Mini Project

Let’s build a small utility for merging default settings with user overrides and printing a summary. This shows rest and spread working together in a realistic way.

function createReport(...tags) {
  const defaults = {
    title: "Weekly Summary",
    author: "System",
    published: false
  };

  const report = {
    ...defaults,
    tags
  };

  return report;
}

const result = createReport("javascript", "functions", "spread");

console.log(result);

This example uses rest to collect all tag arguments into one array and spread to build a new report object from defaults. The output is a plain object with a tags array and the default properties already filled in.

10. Key Points

11. Practice Exercise

Hint: Use ...songs in the function parameters, then spread or direct assignment when building the return object.

function buildPlaylist(...songs) {
  return {
    songs,
    count: songs.length
  };
}

const playlist = buildPlaylist("Intro", "Verse", "Chorus");

console.log(playlist);

12. Final Summary

JavaScript rest and spread syntax share the same three-dot notation, but their jobs are different. Rest gathers values into an array or object, while spread expands values so they can be used individually. Once you recognize the context, the syntax becomes easy to read and use.

In practice, rest is ideal for flexible function parameters and destructuring leftover values, while spread is ideal for copying, combining, and passing data. Just remember that spread is shallow, rest must appear in the right place, and array spread only works with iterables.

If you want to keep learning, next explore destructuring in more depth and compare shallow copies with deep copies so you can use spread confidently in real projects.