
Spread and rest both use ... but do opposite things. Spread expands an iterable into individual elements. Rest collects individual elements into a single array. This post breaks down each one clearly with real examples.
Here's the thing that trips people up: it's the same three dots (...) doing two completely different jobs depending on where you put them. Think of it like a cardboard box. Spread is emptying the box and laying everything out on the table. Rest is collecting everything scattered on the table and putting it into the box. Same motion, opposite direction.
Spread takes something iterable (an array, a string, an object) and expands it into individual pieces.
┌─────────────────────────────────────┐ │ Spread Operator │ │ │ │ [1, 2, 3] ──► ... ──► 1 2 3 │ │ │ │ One thing expands many │ │ (array) into pieces │ └─────────────────────────────────────┘
javascriptconst fruits = ["apple", "banana"]; const moreFruits = ["mango", ...fruits, "grape"]; console.log(moreFruits); // ["mango", "apple", "banana", "grape"]
Without spread, you'd get a nested array:
javascriptconst wrong = ["mango", fruits, "grape"]; // ["mango", ["apple", "banana"], "grape"] ← not what you want
Spread also makes copying arrays clean and explicit:
javascriptconst original = [1, 2, 3]; const copy = [...original]; copy.push(4); console.log(original); // [1, 2, 3] ← untouched console.log(copy); // [1, 2, 3, 4]
This is a shallow copy. Primitives are copied by value. Nested objects inside the array are still referenced, not duplicated.
Merging arrays becomes one line:
javascriptconst a = [1, 2, 3]; const b = [4, 5, 6]; const merged = [...a, ...b]; // [1, 2, 3, 4, 5, 6]
Spread works on objects too, available since ES2018:
javascriptconst defaults = { theme: "light", lang: "en", fontSize: 14 }; const userPrefs = { theme: "dark", fontSize: 16 }; const config = { ...defaults, ...userPrefs }; // { theme: "dark", lang: "en", fontSize: 16 }
Later properties overwrite earlier ones. userPrefs.theme overwrites defaults.theme. This pattern is everywhere in React for merging props and state updates.
Copying an object with one modification:
javascriptconst user = { name: "Atharv", role: "admin", active: true }; const deactivated = { ...user, active: false }; // user is unchanged, deactivated has active: false
Rest does the opposite. It gathers multiple values into a single array. The key constraint: rest must always be last.
┌───────────────────────────────────────┐ │ Rest Operator │ │ │ │ 1 2 3 4 ──► ... ──► [1,2,3,4] │ │ │ │ many pieces collects one thing │ │ into (array) │ └───────────────────────────────────────┘
The classic use case: a function that accepts any number of arguments.
javascriptfunction sum(...numbers) { return numbers.reduce((total, n) => total + n, 0); } sum(1, 2, 3); // 6 sum(10, 20, 30, 40); // 100
numbers is a real array, not the old arguments object. You can call map, filter, reduce on it directly.
You can mix fixed parameters with rest:
javascriptfunction logWithPrefix(prefix, ...messages) { messages.forEach((msg) => console.log(`[${prefix}] ${msg}`)); } logWithPrefix("INFO", "Server started", "Listening on port 3000"); // [INFO] Server started // [INFO] Listening on port 3000
prefix captures the first argument. messages collects everything after it. Rest must be last: you can't put fixed parameters after ...rest.
Rest also works in array and object destructuring to collect what's left over:
javascript// Array destructuring const [first, second, ...remaining] = [1, 2, 3, 4, 5]; // first = 1, second = 2, remaining = [3, 4, 5] // Object destructuring const { name, role, ...rest } = { name: "Atharv", role: "admin", active: true, plan: "pro", }; // name = "Atharv", role = "admin" // rest = { active: true, plan: "pro" }
The object rest pattern is especially useful when you need to extract specific keys and pass the remainder somewhere else:
javascriptfunction createUser({ password, ...safeData }) { // password is extracted and handled separately // safeData = everything except password saveToDatabase(safeData); }
Same syntax. Context determines the job.
┌───────────────────────────────────────────────────────────┐ │ Where you see ... │ ├─────────────────────────────┬─────────────────────────────┤ │ Spread │ Rest │ ├─────────────────────────────┼─────────────────────────────┤ │ On the right side of = │ On the left side of = │ │ const b = [...a] │ const [x, ...rest] = a │ │ │ │ │ In a function call │ In a function definition │ │ fn(...args) │ function fn(...args) {} │ │ │ │ │ Expanding into a target │ Collecting into an array │ │ Many ──► many │ Many ──► one │ └─────────────────────────────┴─────────────────────────────┘
The mental shortcut: if ... is producing values into a context (array literal, object literal, function call), it's spread. If ... is receiving values from a context (function parameters, destructuring), it's rest.
Cloning without mutation:
javascript// Prefer this over Object.assign({}, obj) const updated = { ...existingRecord, updatedAt: new Date() };
Passing array elements as function arguments:
javascriptconst nums = [5, 1, 8, 3]; Math.max(...nums); // 8 // Without spread: Math.max(5, 1, 8, 3) — you'd have to write them out
Omitting keys from an object:
javascriptconst { password, __v, ...publicUser } = fullUserDocument; // publicUser has everything except password and __v
Flexible API functions:
javascriptfunction applyMiddleware(app, ...middlewares) { middlewares.forEach((mw) => app.use(mw)); } applyMiddleware(app, cors(), helmet(), rateLimiter);
... syntax but do opposite things.Once the expanding-vs-collecting mental model clicks, you'll start seeing these patterns everywhere in modern JavaScript. They're in React props, Express middleware chains, utility functions, and API handlers. At that point the syntax stops being a puzzle and starts being a tool.
Related posts based on tags, category, and projects
Destructuring is a concise syntax for pulling values out of arrays and objects into individual variables. This post covers array destructuring, object destructuring, default values, and the real-world patterns where this shines.
Template literals, introduced in ES6, replace messy string concatenation with a cleaner, more readable syntax. This post covers the problems they solve, how they work, and where they shine in modern JavaScript.
Variables are the foundation of every JavaScript program they let you store, label, and reuse data. This post walks you through what variables are, how to declare them, the seven primitive data types, and the three non-primitive types: objects, arrays, and functions.
Flattening arrays means collapsing nested structures into a single level. This post covers what nested arrays are, why you'd want to flatten them, and every real approach you'll encounter, including the ones that show up in interviews.