Destructuring, spread and rest

Unpack values from arrays and objects in one line, copy and merge with spread, and gather leftover values with rest.

  • Beginner
  • 6 min read
  • Updated 17 Oct 2026

What you will learn

  • Destructure function parameters
  • Merge objects with spread
  • Collect remaining arguments with rest parameters
  • Set defaults while destructuring

Before you start

Destructuring pulls values out of arrays and objects into variables. Spread does the opposite, expanding an array or object into a new one. Rest collects "everything else" into one variable. All three use short syntax you will see in almost every modern codebase, so it pays to read them fluently.

Object destructuring

Instead of reading properties one at a time:

JavaScript
const course = { title: 'JavaScript Complete Course', level: 'Beginner', lessons: 54 };

// The long way:
// const title = course.title;
// const level = course.level;

you can unpack them in one statement. The variable names match the property names:

JavaScript
const { title, level } = course;
console.log(title); // → JavaScript Complete Course
console.log(level); // → Beginner

Properties you do not mention are ignored. A property that does not exist gives undefined.

Renaming and defaults

Use a colon to store a property under a different name, and = to give a default for when the property is undefined:

JavaScript
const { title: courseTitle, updated = 'unknown' } = course;
console.log(courseTitle); // → JavaScript Complete Course
console.log(updated);     // → unknown

Read title: courseTitle as "take title and put it in courseTitle". The default only applies to undefined, not to null, 0 or an empty string.

Nested objects

Destructuring patterns can mirror nested data:

JavaScript
const user = {
  name: 'Amira',
  settings: { theme: 'dark', fontSize: 18 },
};

const { name, settings: { theme } } = user;
console.log(name, theme); // → Amira dark

Here settings is a path, not a variable. Only name and theme are created. If user.settings were missing, this would throw a TypeError, so add a default when the nested object is optional: settings: { theme } = {}.

Array destructuring

Arrays destructure by position rather than by name:

JavaScript
const scores = [92, 78, 85];
const [first, second] = scores;
console.log(first, second); // → 92 78

Skip positions with an empty slot, and swap two variables without a temporary one:

JavaScript
const [, , third] = scores;
console.log(third); // → 85

let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // → 2 1

Array destructuring works with anything iterable, so you will also see it with Object.entries (next lesson) and with functions that return two values:

JavaScript
function minMax(numbers) {
  return [Math.min(...numbers), Math.max(...numbers)];
}

const [low, high] = minMax(scores);
console.log(low, high); // → 78 92

Destructuring parameters

This is where destructuring earns its keep. A function that takes an options object can unpack it in the parameter list, with defaults:

JavaScript
function formatLesson({ title, minutes = 15, level = 'Beginner' }) {
  return `${title}: ${minutes} min, ${level}`;
}

console.log(formatLesson({ title: 'Closures', minutes: 20, level: 'Intermediate' }));
// → Closures: 20 min, Intermediate
console.log(formatLesson({ title: 'Variables' }));
// → Variables: 15 min, Beginner

Options objects make calls self-describing, and the order of properties does not matter. To allow calling with no argument at all, default the whole object too:

JavaScript
function createToast({ message = 'Saved', duration = 3000 } = {}) {
  return `${message} for ${duration}ms`;
}

console.log(createToast()); // → Saved for 3000ms

Without = {}, createToast() would throw, because you cannot destructure undefined.

Spread with arrays

Three dots before an array expand it into individual values:

JavaScript
const beginner = ['HTML', 'CSS'];
const advanced = ['Accessibility', 'JavaScript'];

const all = [...beginner, ...advanced];
console.log(all); // → ['HTML', 'CSS', 'Accessibility', 'JavaScript']

const copy = [...beginner];
copy.push('Responsive');
console.log(beginner); // → ['HTML', 'CSS'] (unchanged)

console.log(Math.max(...[3, 9, 4])); // → 9

Spread works on any iterable, so [...'hey'] gives ['h', 'e', 'y'] and [...document.querySelectorAll('li')] turns a NodeList into an array.

Spread with objects

Spread in an object literal copies an object's own properties into a new object:

JavaScript
const defaults = { theme: 'light', fontSize: 16, motion: true };
const saved = { theme: 'dark' };

const settings = { ...defaults, ...saved };
console.log(settings); // → { theme: 'dark', fontSize: 16, motion: true }

When two objects have the same key, the later one wins. That is why defaults go first and overrides after. You can also override a single property while copying:

JavaScript
const updatedSettings = { ...settings, fontSize: 18 };
console.log(updatedSettings.fontSize); // → 18
console.log(settings.fontSize);        // → 16

This "copy with a change" pattern is how you update state without mutating the original.

Spread makes a shallow copy. Nested objects and arrays are shared, not copied. Changing copy.settings.theme would also change the original. The next lesson covers deep copies with structuredClone.

Rest: collect the remainder

Rest uses the same three dots, but on the receiving side. It gathers leftover values into an array or object.

Rest parameters

JavaScript
function total(...prices) {
  return prices.reduce((sum, price) => sum + price, 0);
}

console.log(total(4, 5.5, 10)); // → 19.5
console.log(total());           // → 0

prices is a real array. Combine fixed parameters with a rest parameter, which must come last:

JavaScript
function log(level, ...messages) {
  console.log(`[${level}]`, messages.join(' '));
}

log('info', 'Saved', '3 items'); // → [info] Saved 3 items

Rest parameters replace the old arguments object, which is not a real array and does not exist in arrow functions.

Rest in destructuring

JavaScript
const [winner, ...others] = ['Ana', 'Ben', 'Cy'];
console.log(winner, others); // → Ana ['Ben', 'Cy']

const { password, ...safeUser } = { id: 7, name: 'Leo', password: 'hunter2' };
console.log(safeUser); // → { id: 7, name: 'Leo' }

Object rest is a neat way to drop a property before logging or sending an object, without changing the original.

Spread or rest?

The dots look identical. The position tells you which it is:

Where the dots appearNameWhat happens
In a function call, array literal or object literalSpreadValues are expanded out
In a parameter list or on the left of =RestValues are gathered in

Summary

  • Object destructuring unpacks by name; array destructuring unpacks by position.
  • Use : to rename and = for defaults that apply when a value is undefined.
  • Destructure options objects in parameters, and default the whole object with = {}.
  • Spread expands arrays and objects into new ones; with objects, later keys win.
  • Spread copies are shallow. Rest gathers remaining values into an array or object.

Further reading