Destructuring assignment lets you unpack values from arrays or properties from objects into distinct variables — in a single, readable line. Introduced in ES2015, it has become one of the most-used JavaScript features in modern codebases.
Quick-reference table
| Pattern | Syntax | What it does |
|---|---|---|
| Array destructuring | const [a, b] = arr |
Unpacks by position |
| Object destructuring | const { x, y } = obj |
Unpacks by key name |
| Default value | const [a = 0] = arr |
Falls back if value is undefined |
| Rename | const { x: newName } = obj |
Unpacks to a different variable name |
| Rest element | const [first, ...rest] = arr |
Gathers remaining items |
| Skip items | const [, second] = arr |
Ignores position with empty comma |
| Nested | const { a: { b } } = obj |
Digs into nested structures |
| Swap variables | [a, b] = [b, a] |
No temp variable needed |
| Function params | function f({ x, y }) {} |
Destructures argument inline |
| Computed key | const { [key]: val } = obj |
Dynamic property name |
Array destructuring
Basic syntax
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first); // 'red'
console.log(second); // 'green'
Skipping elements
Use empty commas to skip positions:
const [, , third] = ['a', 'b', 'c'];
console.log(third); // 'c'
Default values
A default is used only when the unpacked value is undefined (not null, not 0, not ''):
const [a = 10, b = 20] = [5];
console.log(a); // 5 — value present, default ignored
console.log(b); // 20 — undefined, default kicks in
Rest element
The rest element (...name) must always be last:
const [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
Swapping variables
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
No temporary variable required.
Iterating with index
const fruits = ['apple', 'banana', 'cherry'];
for (const [index, value] of fruits.entries()) {
console.log(index, value);
}
// 0 'apple'
// 1 'banana'
// 2 'cherry'
Object destructuring
Basic syntax
const user = { name: 'Ana', age: 30, role: 'admin' };
const { name, age } = user;
console.log(name); // 'Ana'
console.log(age); // 30
Renaming variables
Use originalKey: newName:
const { name: userName, age: userAge } = user;
console.log(userName); // 'Ana'
This is useful when the property name would clash with an existing variable.
Default values
const { name, score = 0 } = { name: 'Ana' };
console.log(score); // 0 — key missing, default used
Combine renaming and defaults:
const { name: userName = 'Guest' } = {};
console.log(userName); // 'Guest'
Rest properties
const { role, ...rest } = { name: 'Ana', age: 30, role: 'admin' };
console.log(role); // 'admin'
console.log(rest); // { name: 'Ana', age: 30 }
Useful for stripping a specific key before passing an object on.
Computed property names
When the key is dynamic:
const key = 'score';
const { [key]: value } = { score: 42 };
console.log(value); // 42
Nested destructuring
Nested objects
const config = {
server: {
host: 'localhost',
port: 3000,
},
};
const { server: { host, port } } = config;
console.log(host); // 'localhost'
console.log(port); // 3000
// Note: `server` is NOT declared as a variable here
Nested arrays
const matrix = [[1, 2], [3, 4]];
const [[a, b], [c, d]] = matrix;
console.log(a, b, c, d); // 1 2 3 4
Mixed nesting
const data = {
users: [
{ id: 1, name: 'Ana' },
{ id: 2, name: 'Bob' },
],
};
const { users: [firstUser, secondUser] } = data;
console.log(firstUser.name); // 'Ana'
console.log(secondUser.name); // 'Bob'
Destructuring in function parameters
Instead of accessing opts.host and opts.port inside the function body, destructure the parameter directly:
// Without destructuring
function connect(opts) {
console.log(opts.host, opts.port);
}
// With destructuring
function connect({ host = 'localhost', port = 3000 } = {}) {
console.log(host, port);
}
connect({ host: 'example.com' }); // 'example.com' 3000
connect(); // 'localhost' 3000 ← `= {}` makes opts optional
The = {} at the end makes the whole argument optional — without it, calling connect() with no argument throws TypeError: Cannot destructure property 'host' of undefined.
Array parameter destructuring
function sum([a, b, c = 0]) {
return a + b + c;
}
console.log(sum([1, 2, 3])); // 6
console.log(sum([1, 2])); // 3
Destructuring with for...of
const entries = [
['name', 'Ana'],
['age', 30],
];
for (const [key, value] of entries) {
console.log(`${key}: ${value}`);
}
// name: Ana
// age: 30
This is exactly how Object.entries() is used in practice:
const obj = { a: 1, b: 2, c: 3 };
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
Destructuring in imports and exports
ES module destructuring is not "true" destructuring assignment — it uses the same syntax but the semantics are different (live bindings, not copies). Still, visually identical:
// Named imports look like object destructuring:
import { useState, useEffect } from 'react';
// CommonJS destructuring:
const { readFile, writeFile } = require('fs/promises');
Real-world patterns
React props
// Before
function Button(props) {
return <button className={props.variant}>{props.label}</button>;
}
// After
function Button({ variant = 'primary', label, onClick }) {
return <button className={variant} onClick={onClick}>{label}</button>;
}
API response parsing
const response = await fetch('/api/user/1');
const { id, name, email, role = 'viewer' } = await response.json();
Swap without temp variable
let a = 'hello', b = 'world';
[a, b] = [b, a];
// a = 'world', b = 'hello'
Extracting first/rest from array
const [first, second, ...others] = fetchedItems;
renderHighlight(first, second);
renderList(others);
Passing only needed fields
function createUser({ name, email }) {
return db.insert({ name, email }); // ignores extra fields from input
}
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
const { } = null |
TypeError: Cannot destructure null |
Guard: const { x } = obj ?? {} |
const [a] = undefined |
TypeError: undefined is not iterable |
Guard or default: const [a] = arr ?? [] |
Forgetting = {} on param |
Function throws when called without argument | function f({ x } = {}) {} |
| Renaming AND defaulting order | const { x = 0: name } — invalid syntax |
Correct order: const { x: name = 0 } |
null bypasses default |
const [a = 1] = [null] → a is null, not 1 |
Defaults only apply to undefined |
Declaring server via nested destructure |
const { server: { host } } = config — server is not declared |
Access config.server.host directly if you need server too |
TypeScript with destructuring
TypeScript infers types automatically, but you can be explicit:
// Object — type is inferred
const { name, age }: { name: string; age: number } = user;
// Array — tuple annotation
const [status, data]: [number, string] = getResult();
// Function parameter
function greet({ name, age = 0 }: { name: string; age?: number }): string {
return `${name} is ${age}`;
}
// With interface
interface Config {
host: string;
port?: number;
}
function start({ host, port = 8080 }: Config) {
console.log(host, port);
}
FAQ
1. Does destructuring copy values or create references? For primitives (strings, numbers, booleans) it copies the value. For objects and arrays it copies the reference — the same as a normal variable assignment. Mutating a destructured object property mutates the original.
2. Can I destructure a Map or Set?
Not directly with object syntax — they have no own enumerable properties. You can array-destructure a Map.entries() iterator or [...set] spread:
const [[key, val]] = new Map([['a', 1]]);
const [first] = new Set([10, 20, 30]);
3. What's the difference between rest in arrays vs objects?
Array rest (...rest) gathers remaining elements (ordered, array). Object rest (...rest) gathers remaining properties (unordered, object). Both produce a new array/object — they do not affect the original.
4. Can I use destructuring on the left side of a regular assignment (not const/let)?
Yes, but you need parentheses to avoid the parser treating { as a block:
let a, b;
({ a, b } = { a: 1, b: 2 }); // parentheses required
5. Is there a performance cost? Negligible in practice — modern JS engines optimise destructuring. Don't avoid it for performance reasons; the readability gain is worth it.
6. Does destructuring work with arguments?
arguments is array-like but not a true array, so const [a, b] = arguments works. However, using rest params is clearer: function f(...args) { const [a, b] = args; }.