The power of the spread and rest syntax in JavaScript

2 min read
0 views

It’s a fair bet that most web developers regularly encounter the three dots (...) in their code. Sometimes it magically copies arrays, sometimes it gathers up function arguments, and sometimes it shows up in object literals like it’s casting a spell.

Say hello to the spread syntax and rest parameter, two simple but powerful features that have helped me make JavaScript cleaner, more expressive, and less error-prone.

What’s the difference between the two?

Spread and rest might share the same ... syntax, but they behave differently depending on where they’re used.

Syntax Name Purpose Example
... Spread Expands elements from an iterable const copy = [...arr]
... Rest Gathers multiple elements function(...args) {}

Spread syntax: expanding things out

Spread is used to unpack values from arrays, objects, or other iterable structures. For instance:

Cloning arrays (shallow copy)

const numbers = [1, 2, 3];
const copy = [...numbers];

Now you have a shallow copy of numbers, not a reference, which is perfect for avoiding side effects.

Combining arrays

const more = [4, 5];
const all = [...numbers, ...more]; // [1, 2, 3, 4, 5]

No need for .concat() anymore!

Spreading into function arguments

const coords = [10, 20];
function move(x, y) {
  console.log(`Moving to (${x}, ${y})`);
}

move(...coords); // Same as move(10, 20)

Spread syntax works beautifully with arrays and DOM methods like querySelectorAll.

Copying & updating objects (React devs, take note!)

const user = { name: "Sam", age: 30 };
const updatedUser = { ...user, age: 31 };

This creates a new object where age is overridden — great for immutable updates in frameworks like React.

Strings are iterable, too!

const chars = [..."hello"]; // ['h', 'e', 'l', 'l', 'o']

Rest parameters: gathering things up

Rest collects multiple values into an array or object. It’s especially useful in function signatures and destructuring.

In function parameters

function logAll(...messages) {
  messages.forEach(msg => console.log(msg));
}

logAll("Hello", "World", "Again");
// logs each message

This replaces the old arguments object and is far more flexible.

Array destructuring with rest

const [first, ...others] = [1, 2, 3, 4];
console.log(first);  // 1
console.log(others); // [2, 3, 4]

Object destructuring with rest

const { id, ...info } = { id: 1, name: "Sam", age: 30 };
console.log(id);   // 1
console.log(info); // { name: "Sam", age: 30 }

Real world use cases

React: updating state immutably

setUser(prev => ({ ...prev, age: prev.age + 1 }));

Spread makes it easy to update state without mutating the original object.

Form handling: merging fields

const handleChange = (e) => {
  setForm(prev => ({ ...prev, [e.target.name]: e.target.value }));
};

This pattern is everywhere in React forms.

Utility functions

function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}

Rest parameters let you accept any number of arguments. That’s a perfect use for utilities like this.

Watch out for these common pitfalls

Shallow copies only

Spread copies only the top level of arrays and objects. Nested structures remain references.

const nested = { a: { b: 2 } };
const copy = { ...nested };
copy.a.b = 99;
console.log(nested.a.b); // 99 — still linked!

Order matters in object spread

const user = { name: "Sam" };
const updated = { ...user, name: "Alex" }; // name is "Alex"

Properties on the right override those on the left. Always place updates last.

Putting it all together

Spread and rest syntax may look small, but they unlock huge expressive power in JavaScript. From simplifying function arguments to immutably updating state, they help developers write cleaner, more predictable code.

You’ll see ... everywhere — in React, in utility libraries, and in vanilla JS. Understanding how and where to use it is a must for any JavaScript developer today.