Destructuring (Objects and Arrays)
You're reading someone else's React code and see this at the top of a component:
const { user: { name, email }, isLoading = false } = props;
What just happened? If you've ever felt like modern JavaScript is full of mysterious shortcuts that everyone except you understands, destructuring is probably the biggest culprit. Once it clicks, you'll wonder how you ever lived without it.
What is Destructuring?
Destructuring is a syntax that lets you unpack values from arrays or properties from objects into distinct variables — all in a single line.
Instead of accessing properties one by one:
const name = user.name;
const email = user.email;
const age = user.age;
You can extract them all at once:
const { name, email, age } = user;
Think of it as the reverse of building an object. When you create an object, you pack values into a structure. When you destructure, you unpack them.
Why Does This Exist?
Before ES6 (2015), extracting values from objects and arrays was tedious. Developers wrote repetitive code like:
var config = getConfig();
var host = config.host;
var port = config.port;
var timeout = config.timeout;
This got worse with nested objects and function parameters. Destructuring was added to make code more concise and readable — and it became essential for working with React props, API responses, and modern JavaScript patterns.
Let's See It in Action
Basic Object Destructuring
const user = {
name: "Dima",
role: "Developer",
country: "Germany"
};
// Extract specific properties into variables
const { name, role } = user;
console.log(name); // Output: "Dima"
console.log(role); // Output: "Developer"
// 'country' wasn't extracted — it's still in 'user' but not a separate variable
The variable names must match the property names exactly. The order doesn't matter — only the names do.
Basic Array Destructuring
const colors = ["red", "green", "blue"];
// Position matters for arrays
const [first, second, third] = colors;
console.log(first); // Output: "red"
console.log(second); // Output: "green"
console.log(third); // Output: "blue"
Unlike objects, arrays are destructured by position, not name. You can name the variables whatever you want.
Skipping Elements in Arrays
const scores = [95, 87, 72, 88, 91];
// Skip the middle values with empty slots
const [highest, , , , lowest] = scores;
console.log(highest); // Output: 95
console.log(lowest); // Output: 91
// Or grab just the first and ignore the rest
const [winner] = scores;
console.log(winner); // Output: 95
Renaming Variables (Object Destructuring)
What if the property name doesn't work for you? Rename it:
const apiResponse = {
user_name: "dima_codes",
user_id: 42,
is_active: true
};
// Rename snake_case to camelCase
const { user_name: username, user_id: userId, is_active: isActive } = apiResponse;
console.log(username); // Output: "dima_codes"
console.log(userId); // Output: 42
// user_name is NOT a variable — only username is
The syntax is { originalName: newName }. Read it as "take user_name and call it username."
Default Values
When a property might not exist, provide a fallback:
const settings = {
theme: "dark"
// 'language' is missing
};
const { theme, language = "en", notifications = true } = settings;
console.log(theme); // Output: "dark" (from object)
console.log(language); // Output: "en" (default, since missing)
console.log(notifications); // Output: true (default, since missing)
Defaults only kick in when the value is undefined, not null:
const { value = "default" } = { value: null };
console.log(value); // Output: null (NOT "default")
const { other = "default" } = { other: undefined };
console.log(other); // Output: "default"
Real-World Usage: React Components
Destructuring is everywhere in React:
// Props destructuring in function parameters
function UserCard({ name, avatar, role = "Member", onEdit }) {
return (
<div className="card">
<img src={avatar} alt={name} />
<h2>{name}</h2>
<span>{role}</span>
<button onClick={onEdit}>Edit</button>
</div>
);
}
// Usage
<UserCard
name="Dima"
avatar="/dima.jpg"
onEdit={() => console.log("Edit clicked")}
/>
The component receives a single props object, but destructuring in the function signature makes each prop available directly.
Real-World Usage: API Responses
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const { data: user, meta: { totalPages, currentPage } } = await response.json();
// Now we have:
// - user (renamed from 'data')
// - totalPages (extracted from nested 'meta')
// - currentPage (extracted from nested 'meta')
return { user, totalPages, currentPage };
}
Nested Destructuring
You can destructure multiple levels deep:
const company = {
name: "TechCorp",
address: {
city: "Berlin",
country: "Germany",
coordinates: {
lat: 52.52,
lng: 13.405
}
}
};
// Extract nested values
const {
name,
address: {
city,
coordinates: { lat, lng }
}
} = company;
console.log(name); // Output: "TechCorp"
console.log(city); // Output: "Berlin"
console.log(lat); // Output: 52.52
// Note: 'address' and 'coordinates' are NOT variables here!
// Only the "leaf" values become variables
The Rest Pattern
Grab specific items and collect the rest:
// With arrays
const [gold, silver, ...others] = ["Alice", "Bob", "Charlie", "Diana", "Eve"];
console.log(gold); // Output: "Alice"
console.log(silver); // Output: "Bob"
console.log(others); // Output: ["Charlie", "Diana", "Eve"]
// With objects
const { password, ...safeUser } = {
name: "Dima",
email: "dima@example.com",
password: "secret123"
};
console.log(safeUser); // Output: { name: "Dima", email: "dima@example.com" }
// password is extracted and excluded from safeUser
This is incredibly useful for removing sensitive fields or separating known properties from dynamic ones.
Watch Out: Destructuring Undefined/Null
// ❌ This will crash
const { name } = undefined;
// TypeError: Cannot destructure property 'name' of 'undefined'
const [first] = null;
// TypeError: null is not iterable
Always ensure the source exists:
// ✅ Safe with default empty object
const { name } = undefined || {};
console.log(name); // Output: undefined (no crash)
// ✅ Or use optional chaining + nullish coalescing
const data = null;
const { name: userName } = data ?? {};
console.log(userName); // Output: undefined (no crash)
Watch Out: Variable Declaration Required
// ❌ Syntax error — looks like a block, not destructuring
{ name, age } = user;
// ✅ Wrap in parentheses when assigning to existing variables
let name, age;
({ name, age } = user);
Without const/let/var, JavaScript thinks { starts a code block. Parentheses fix this.
Interview Challenge: Swapping Variables
let a = 1;
let b = 2;
// What does this do?
[a, b] = [b, a];
console.log(a); // Output: ?
console.log(b); // Output: ?
Answer: a is now 2, b is now 1. This is a clean way to swap values without a temporary variable. The right side creates an array [2, 1], then destructuring assigns those values back to a and b.
Interview Challenge: What Gets Logged?
const config = {
api: {
host: "localhost",
port: 3000
}
};
const { api: { host, port }, api } = config;
console.log(host); // Output: ?
console.log(api); // Output: ?
Answer: host is "localhost", and api is { host: "localhost", port: 3000 }. You can extract nested properties AND keep a reference to the parent object in the same destructuring. The order doesn't matter.
Advanced: Function Parameter Defaults
Combine destructuring with default parameters for clean APIs:
function createUser({
name,
role = "user",
active = true,
permissions = []
} = {}) { // The = {} handles when no argument is passed at all
return { name, role, active, permissions };
}
// All of these work:
createUser({ name: "Dima" });
// { name: "Dima", role: "user", active: true, permissions: [] }
createUser({ name: "Admin", role: "admin", permissions: ["delete"] });
// { name: "Admin", role: "admin", active: true, permissions: ["delete"] }
createUser();
// { name: undefined, role: "user", active: true, permissions: [] }
The outer = {} is crucial — without it, calling createUser() with no arguments would crash.
How to Think About This
Think of destructuring as pattern matching against a shape.
For objects, you're saying: "I expect this shape { name, email }, and I want those pieces as variables." The object must have properties matching those names.
For arrays, you're saying: "I expect this shape [first, second, third], and I want those positions as variables." The array must have elements at those indices.
💡 Remember: Objects destructure by name, arrays destructure by position. Defaults apply when the value is
undefined, notnull.
Test Yourself
Before moving on, make sure you can answer:
- What's the difference between
{ x: y }and{ x = y }in destructuring? - Why does
const { a } = nullthrow an error? - How do you rename a property while destructuring AND provide a default value?
- What does the rest pattern (
...) collect in object vs array destructuring? - How can you destructure function parameters with defaults?
Related Topics
- Spread Operator — The mirror image of destructuring; spreads elements out instead of collecting them
- Default Parameters — Works hand-in-hand with destructuring in function signatures
- Object Shorthand — When
{ name: name }becomes just{ name }, the opposite of destructuring - Optional Chaining — Safely access nested properties without destructuring crashing on undefined
Go Deeper
- MDN: Destructuring Assignment — Comprehensive reference with all edge cases
- javascript.info: Destructuring — Interactive examples and exercises
- ES6 In Depth: Destructuring — Mozilla's deep dive into the feature when it was released
- Axel Rauschmayer: Destructuring — Thorough coverage from "Exploring ES6"