Skip to content
elephantoo

Destructuring & spread

Lesson 12 of 34 14 min read

Unpack arrays and objects, set defaults, rename, and copy or merge with ... spread and rest.


Destructuring unpacks values from arrays and objects into variables. Spread (...) expands them into new arrays, objects or function arguments, and rest (also ...) gathers the leftovers. Together they make modern JavaScript concise — and they're everywhere in React code.

Array destructuring#

Values are assigned by position:

JavaScript
const medals = ["gold", "silver", "bronze"];

const [first, second, third] = medals;
console.log(first, third); // gold bronze

const [winner, , bronzeWinner] = medals; // skip with a hole
console.log(bronzeWinner); // bronze

const [a, b, c, d = "none"] = medals;    // default for missing items
console.log(d); // none

Swapping variables

JavaScript
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1

Rest in arrays

JavaScript
const [head, ...tail] = [10, 20, 30, 40];
console.log(head); // 10
console.log(tail); // [ 20, 30, 40 ]

The rest element must be last.

Functions returning multiple values

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

const [low, high] = minMax([7, 2, 9, 4]);
console.log(low, high); // 2 9

This is exactly how React's const [count, setCount] = useState(0) works.

Object destructuring#

Values are assigned by property name, so order doesn't matter:

JavaScript
const user = { id: 7, name: "Ada", email: "ada@example.com", role: "admin" };

const { name, email } = user;
console.log(name, email); // Ada ada@example.com

Renaming and defaults

JavaScript
const settings = { theme: "dark" };

const { theme: currentTheme, fontSize = 16 } = settings;
console.log(currentTheme, fontSize); // dark 16

Read theme: currentTheme as "take theme and call it currentTheme". Defaults apply only when the value is undefined, not null.

Rest in objects

JavaScript
const { password, ...safeUser } = { id: 1, name: "Ada", password: "hunter2" };
console.log(safeUser); // { id: 1, name: 'Ada' }

A neat way to remove a property without mutating the original.

Nested destructuring

JavaScript
const order = {
  id: 501,
  customer: { name: "Grace", address: { city: "Pune", pin: "411001" } },
  items: [{ sku: "BOOK-1", qty: 2 }],
};

const {
  customer: {
    name: customerName,
    address: { city },
  },
  items: [firstItem],
} = order;

console.log(customerName, city, firstItem.sku); // Grace Pune BOOK-1

Nested destructuring throws if an intermediate value is missing (customer undefined). For uncertain data, provide defaults (customer: { name } = {}) or use optional chaining.

Destructuring in function parameters#

This is where destructuring shines — named, self-documenting options:

JavaScript
function createUser({ name, role = "student", active = true }) {
  return `${name} (${role})${active ? "" : " [inactive]"}`;
}

console.log(createUser({ name: "Ada" }));                   // Ada (student)
console.log(createUser({ name: "Alan", role: "teacher" })); // Alan (teacher)
console.log(createUser({ role: "admin", name: "Grace", active: false })); // Grace (admin) [inactive]

Add = {} so the function can be called with no argument at all:

JavaScript
function connect({ host = "localhost", port = 5432 } = {}) {
  return `${host}:${port}`;
}
console.log(connect());             // localhost:5432
console.log(connect({ port: 3306 })); // localhost:3306

Destructuring works in loops and callbacks too:

JavaScript
const scores = { ada: 91, alan: 78 };
for (const [student, score] of Object.entries(scores)) {
  console.log(`${student}: ${score}`);
}

const people = [{ name: "Ada", age: 36 }, { name: "Alan", age: 41 }];
console.log(people.map(({ name }) => name)); // [ 'Ada', 'Alan' ]

Spread with arrays#

JavaScript
const frontEnd = ["html", "css", "js"];
const backEnd = ["node", "sql"];

const fullStack = [...frontEnd, ...backEnd];
console.log(fullStack); // [ 'html', 'css', 'js', 'node', 'sql' ]

const copy = [...frontEnd];          // shallow copy
const withTs = [...frontEnd, "ts"];  // "push" without mutating
const withFirst = ["a11y", ...frontEnd];

console.log(Math.max(...[3, 9, 4])); // 9 – spread into arguments
console.log([..."hey"]);             // [ 'h', 'e', 'y' ] – any iterable
console.log([...new Set([1, 1, 2, 3, 3])]); // [ 1, 2, 3 ] – remove duplicates

Spread with objects#

JavaScript
const defaults = { theme: "light", lang: "en", fontSize: 16 };
const userPrefs = { theme: "dark" };

const prefs = { ...defaults, ...userPrefs };
console.log(prefs); // { theme: 'dark', lang: 'en', fontSize: 16 }

const bigger = { ...prefs, fontSize: prefs.fontSize + 2 };
console.log(bigger.fontSize, prefs.fontSize); // 18 16

Order matters: later properties overwrite earlier ones. Put defaults first and overrides last.

Immutable updates of nested data

JavaScript
const state = {
  user: { name: "Ada", prefs: { theme: "light" } },
  todos: [{ id: 1, text: "Learn spread", done: false }],
};

const next = {
  ...state,
  user: { ...state.user, prefs: { ...state.user.prefs, theme: "dark" } },
  todos: state.todos.map((t) => (t.id === 1 ? { ...t, done: true } : t)),
};

console.log(state.user.prefs.theme, next.user.prefs.theme); // light dark
console.log(state.todos[0].done, next.todos[0].done);       // false true

Every level you change gets a new copy; everything else is shared. This is the core pattern for updating React state.

Rest vs spread: same dots, opposite jobs#

WhereNameDoes
Function parameters (...args)restgathers arguments into an array
Destructuring const [a, ...others]restgathers remaining items
Array/object literals [...arr], {...obj}spreadexpands items
Function calls fn(...arr)spreadexpands into arguments

Rule of thumb: on the left of = or in a parameter list it gathers (rest); on the right or in a call it expands (spread).

Common mistakes#

  • Destructuring null or undefined: const { a } = null throws a TypeError. Use = {} defaults.
  • Forgetting parentheses when destructuring into existing variables: ({ a, b } = obj); — without them, { starts a block.
  • Expecting spread to deep-copy nested objects.
  • Spreading huge arrays into function arguments (Math.max(...hugeArray)) can exceed the engine's argument limit — use reduce for very large arrays.

What's next#

You can now work fluently with data. Next, take it to the browser: select, create and change elements with the DOM.

Check your understanding

Quick quiz

0/3 answered
  1. 1.What is b after const [a, , b] = [1, 2, 3];?

  2. 2.What does const { title: heading = "Untitled" } = {}; create?

  3. 3.What is { ...{ a: 1, b: 2 }, b: 3 }?

Finished reading?

Mark this lesson complete to track your progress.