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.
- rest collects multiple values into one array or object.
- spread expands an array, object, or iterable into separate values.
- Both are common in function arguments, array literals, object literals, and destructuring.
- They help write shorter, clearer code without manually looping or copying values.
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:
- handle functions with flexible numbers of arguments,
- copy or combine arrays and objects without mutating the original value,
- pick out specific values while keeping the remaining ones together,
- write cleaner code for destructuring data from APIs or application state.
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
- Forwarding arguments from one function to another without writing a custom loop.
- Creating immutable updates to arrays and objects in application state.
- Combining default data with overrides, such as configuration objects.
- Writing utility functions that accept a variable number of inputs.
- Removing selected properties while keeping the rest for logging or API calls.
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
- Object spread is a shallow copy, so nested objects and arrays are still shared.
- Rest parameters only work in function parameter lists and destructuring patterns, not in ordinary expressions.
- Array spread requires an iterable value; plain objects cannot be spread into arrays.
- Object spread copies enumerable own properties, so it does not copy the prototype chain or non-enumerable properties.
- Spread does not preserve special object behavior such as class instances, methods on prototypes, or property descriptors.
- Rest parameters always produce a real array, even if the caller passed an array-like object.
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
- Rest collects values; spread expands values.
- Use rest in function parameters and destructuring.
- Use spread in function calls, arrays, and objects.
- Spread creates a shallow copy, not a deep clone.
- Rest parameters must be last in a parameter list.
- Array spread needs an iterable value.
11. Practice Exercise
- Write a function named buildPlaylist that accepts any number of song titles.
- Use rest parameters to collect the titles into one array.
- Return a new object with a songs property and a count property.
- Expected output: an object like { songs: ["Intro", "Verse", "Chorus"], count: 3 }.
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.