Senior 4 min · March 05, 2026

JavaScript Destructuring — Why `undefined` Crashes Render

Destructuring undefined throws before your try/catch runs.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Array destructuring extracts by position: const [first, second] = arr. Use with useState (returns [value, setter]) and swapping variables.
  • Object destructuring extracts by key name: const { name, age } = user. Order doesn't matter. Rename with colon: const { user_id: userId } = apiResponse.
  • Default values: const { timeout = 3000 } = config only triggers on undefined, not null. API returning null for missing fields breaks this.
  • Nested destructuring: const { data: { user } } = response. Limit to 2 levels — deeper is unreadable. Break into steps.
  • Production trap: destructuring undefined/null throws "Cannot destructure property of undefined". Add fallback: const { name } = user ?? {}.
  • Biggest mistake: confusing default value with renaming — const { timeout: 3000 } = config is trying to assign to variable named 3000 (invalid). Correct: const { timeout: connectionTimeout = 3000 } = config.
Plain-English First

Imagine you ordered a pizza combo: a pizza, a drink, and a dessert all arrive in one box. Destructuring is like opening that box and immediately putting the pizza on a plate, the drink in a glass, and the dessert in a bowl — each thing goes exactly where you want it, in one smooth move. Without destructuring, you'd open the box, then separately reach in for each item. It's the same stuff — you're just unpacking it faster and more cleanly.

Every JavaScript codebase you'll ever work in — React components, REST API responses, configuration objects, utility functions — ships data bundled together inside arrays and objects. The moment you need to actually use that data, you're pulling values out one by one. That unpacking code adds up fast, and it clutters the parts of your code that should be focused on logic, not bookkeeping.

Destructuring, introduced in ES6, is JavaScript's answer to that problem. It lets you declare exactly what you want from an array or object, and the runtime hands it to you — renamed, with defaults, even nested — in a single line. It's not magic syntax sugar; it's a deliberate design choice that makes your intent clearer to every developer who reads your code after you.

By the end of this article you'll know not just how to write destructuring syntax, but when and why to reach for it. You'll handle real API responses, write cleaner function signatures, swap variables without a temp, and confidently destructure nested objects without getting tangled up. You'll also know the three mistakes that trip up almost everyone the first time — before you make them yourself.

Array Destructuring — Position Is Everything

Array destructuring unpacks values by position. The first variable you declare gets the first element, the second gets the second, and so on. The key insight is that you're binding names to slots, not to the values themselves.

Why does that matter? Because it lets you skip elements you don't care about using commas as placeholders, and it lets you capture 'everything else' with the rest operator (...). This is especially powerful when working with functions that return multiple values — a pattern that was historically awkward in JavaScript before destructuring existed.

A classic real-world case: useState in React returns [currentValue, setterFunction] as a two-element array specifically because destructuring makes consuming it so clean. The React team made an API design decision based on this syntax. That's how central destructuring is to modern JavaScript.

Notice also the variable swap example below. Swapping two variables previously required a temporary variable. With array destructuring, it's one line — and the intent is crystal clear.

Performance note: Array destructuring is not zero-cost. It creates temporary variables and iterates over the array. For hot loops (millions of iterations), direct indexed access const a = arr[0]; const b = arr[1] is marginally faster. For normal code, the readability gain outweighs the microscopic performance difference.

io/thecodeforge/destructuring/arrayDestructuring.jsJAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// ----- BASIC ARRAY DESTRUCTURING -----
const rgb = [255, 128, 0];

// Without destructuring — noisy and easy to get indices wrong
const redOld   = rgb[0];
const greenOld = rgb[1];
const blueOld  = rgb[2];

// With destructuring — one line, intent is obvious
const [red, green, blue] = rgb;
console.log(`RGB: ${red}, ${green}, ${blue}`);
// → RGB: 255, 128, 0


// ----- SKIPPING ELEMENTS WITH COMMAS -----
const coordinates = [40.7128, -74.0060, 10]; // lat, lng, altitude

// We only care about lat and lng — skip altitude with a trailing comma
const [latitude, longitude] = coordinates;
console.log(`Location: ${latitude}° N, ${longitude}° W`);
// → Location: 40.7128° N, -74.006° W


// ----- SKIPPING A MIDDLE ELEMENT -----
const topThreeScores = [980, 850, 720];

// Grab first and third — leave a gap for second
const [firstPlace, , thirdPlace] = topThreeScores;
console.log(`Gold: ${firstPlace}, Bronze: ${thirdPlace}`);
// → Gold: 980, Bronze: 720


// ----- REST OPERATOR — COLLECT REMAINING ITEMS -----
const playlist = ['Bohemian Rhapsody', 'Hotel California', 'Stairway to Heaven', 'Wonderwall'];

// First track plays now — everything else goes into a queue
const [nowPlaying, ...queue] = playlist;
console.log('Now playing:', nowPlaying);
console.log('Up next:', queue);
// → Now playing: Bohemian Rhapsody
// → Up next: ['Hotel California', 'Stairway to Heaven', 'Wonderwall']


// ----- SWAPPING VARIABLES — NO TEMP VARIABLE NEEDED -----
let playerOneScore = 42;
let playerTwoScore = 87;

// The right-hand side is evaluated first, then assigned
[playerOneScore, playerTwoScore] = [playerTwoScore, playerOneScore];
console.log(`P1: ${playerOneScore}, P2: ${playerTwoScore}`);
// → P1: 87, P2: 42


// ----- DEFAULT VALUES — SAFE UNPACKING -----
const userPreferences = ['dark']; // only theme is set, fontSize is missing

// 'md' is the fallback if the second element is undefined
const [theme = 'light', fontSize = 'md'] = userPreferences;
console.log(`Theme: ${theme}, Font: ${fontSize}`);
// → Theme: dark, Font: md
Output
RGB: 255, 128, 0
Location: 40.7128° N, -74.006° W
Gold: 980, Bronze: 720
Now playing: Bohemian Rhapsody
Up next: [ 'Hotel California', 'Stairway to Heaven', 'Wonderwall' ]
P1: 87, P2: 42
Theme: dark, Font: md
Default values only fire on undefined, not null
A default like const [count = 0] = [null] gives you null, not 0. Defaults only kick in when the slot is strictly undefined — missing entirely, or explicitly set to undefined. null means 'intentionally empty' and it passes through as-is. Keep that in mind when consuming API responses where null and missing fields mean different things.
Production Insight
A team used array destructuring to get const [error, data] = result from a Promise.allSettled wrapper. When the promise rejected, error was an Error object and data was undefined. But when the promise resolved with null (valid response), data became null, and subsequent code that expected an object destructured data again: const { userId } = data crashed because null can't be destructured.
Fix: const { userId } = data ?? {}. Always add ?? {} when destructuring potentially null/undefined values.
Key Takeaway
Array destructuring binds by position — adding or removing elements from the source breaks your bindings silently.
Use array destructuring for tuples (fixed order, small size) — useState, coordinates, split results.
Rule: Prefer object destructuring when the data source is under someone else's control (APIs, config files).
Choose Array vs Object Destructuring
IfData source is a tuple with fixed order (e.g., useState, coordinates, RGB values)
UseUse array destructuring. Clean and idiomatic. Position is meaningful.
IfData source is an object with named properties (API response, config object)
UseUse object destructuring. Order doesn't matter, renaming is easy, adding fields doesn't break existing code.
IfYou need to skip many elements (more than 2 commas) — e.g., const [,,,fourth] = arr
UseArray destructuring becomes unreadable. Consider using .at() or manual indexing instead.
IfReturning multiple values from a function you control
UseUse array destructuring for small, stable return sets (2-3 values). Use object destructuring for larger or evolving return values.

Object Destructuring — Names Over Positions

Object destructuring binds by key name, not position. That distinction is what makes it so robust for consuming API data — it doesn't matter what order the keys arrive in, you just ask for what you need by name.

The syntax mirrors the object literal syntax on purpose: curly braces on the left side of the assignment, keys inside. When the key name on the object matches the variable name you want, it's one-to-one. When you want a different local name — say the API sends user_name but your codebase uses camelCase — you rename with a colon.

Object destructuring also shines in function parameters. Instead of a function receiving a big config object and then pulling properties off it line by line, you destructure right in the parameter list. The function signature becomes self-documenting: anyone reading it immediately sees exactly what fields the function depends on.

One power move is combining renaming with defaults in the same expression. It looks dense at first, but once it clicks it's incredibly readable — the variable name, the source key, and its fallback value are all in one place.

State management note: In Redux reducers, object destructuring is the standard way to extract action payloads: const { type, payload } = action. In React props, destructuring in the component signature makes dependencies explicit: function Button({ onClick, children, variant = 'primary' }).

io/thecodeforge/destructuring/objectDestructuring.jsJAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// ----- BASIC OBJECT DESTRUCTURING -----
const blogPost = {
  title: 'JavaScript Destructuring Explained',
  author: 'Jordan Lee',
  publishedAt: '2024-03-15',
  readTimeMinutes: 8
};

// Pull out only what we need — other keys are untouched
const { title, author, readTimeMinutes } = blogPost;
console.log(`"${title}" by ${author} — ${readTimeMinutes} min read`);
// → "JavaScript Destructuring Explained" by Jordan Lee — 8 min read


// ----- RENAMING ON EXTRACTION -----
// Imagine this came from a legacy API with snake_case keys
const apiUser = {
  user_id: 'u_8821',
  display_name: 'Alex Morgan',
  is_premium: true
};

// Rename: sourceKey: localVariableName
const { user_id: userId, display_name: displayName, is_premium: isPremium } = apiUser;
console.log(userId, displayName, isPremium);
// → u_8821 Alex Morgan true


// ----- DEFAULT VALUES WITH RENAMING -----
const serverConfig = {
  host: 'db.production.io'
  // port and timeout are missing — they might not always be sent
};

// Rename AND provide a fallback — colon renames, equals sets default
const {
  host: dbHost,
  port: dbPort = 5432,
  timeout: dbTimeout = 3000
} = serverConfig;

console.log(`Connecting to ${dbHost}:${dbPort} (timeout: ${dbTimeout}ms)`);
// → Connecting to db.production.io:5432 (timeout: 3000ms)


// ----- DESTRUCTURING IN FUNCTION PARAMETERS -----
// Before: you'd receive `options` and do options.width, options.height...
// After: your signature is self-documenting

function renderCard({ title, imageUrl, description = 'No description provided.', isPinned = false }) {
  // This function makes it immediately clear what shape of object it expects
  const pinLabel = isPinned ? '📌 ' : '';
  return `${pinLabel}${title}: ${description} [${imageUrl}]`;
}

const card = {
  title: 'Grand Canyon Sunset',
  imageUrl: 'https://cdn.example.com/gc-sunset.jpg'
  // description and isPinned are absent — defaults will apply
};

console.log(renderCard(card));
// → Grand Canyon Sunset: No description provided. [https://cdn.example.com/gc-sunset.jpg]


// ----- REST IN OBJECT DESTRUCTURING -----
const fullProfile = {
  id: 'p_4491',
  email: 'alex@example.com',
  bio: 'Engineer and coffee enthusiast',
  followers: 1204,
  following: 387
};

// Separate the identity fields from the stats — common when building a UI
const { id, email, ...profileStats } = fullProfile;
console.log('Identity:', id, email);
console.log('Stats:', profileStats);
// → Identity: p_4491 alex@example.com
// → Stats: { bio: 'Engineer and coffee enthusiast', followers: 1204, following: 387 }
Output
"JavaScript Destructuring Explained" by Jordan Lee — 8 min read
u_8821 Alex Morgan true
Connecting to db.production.io:5432 (timeout: 3000ms)
Grand Canyon Sunset: No description provided. [https://cdn.example.com/gc-sunset.jpg]
Identity: p_4491 alex@example.com
Stats: { bio: 'Engineer and coffee enthusiast', followers: 1204, following: 387 }
Interview Gold: Why destructure in function params?
Destructuring in function parameters has two concrete benefits interviewers love: (1) it enforces that callers pass a named-property interface, which is more resilient than positional arguments when a function grows — you can add optional params without breaking existing callers. (2) It makes the function signature a live contract — glancing at function save({ userId, content, isDraft = false }) tells you everything about what the function needs, without opening the function body.
Production Insight
A team had a function function createUser(options) with 15 possible options. Every caller had to know which options existed. The function body was 200 lines of const name = options.name; const email = options.email;.
After refactoring to function createUser({ name, email, isActive = true, role = 'user', ...rest }), the signature alone documented the API. The team caught 3 bugs where callers passed userName instead of name — the destructuring assignment created undefined and the default didn't apply because options.userName existed but options.name didn't. The fix was to rename the source key: { name: userName }.
Key Takeaway
Object destructuring binds by key name — order doesn't matter, making it robust for evolving APIs.
Rename with colon: { sourceKey: localName }. Default with equals: { key = 'default' }. Combine: { sourceKey: localName = 'default' }.
Rule: Destructure in function parameters to create self-documenting, resilient APIs.

Nested Destructuring and Real API Response Patterns

Real-world data is rarely flat. A typical API response has objects inside objects, arrays inside objects, or arrays of objects. Nested destructuring lets you reach multiple levels deep in a single declaration — but it comes with a cost: readability degrades fast if you go too deep.

The rule of thumb most senior devs follow is two levels max in a single destructure. Beyond that, break it into two separate statements. Your future self — and your teammates — will thank you.

Nested destructuring is especially valuable when you're working with a consistent response shape, like every response from the same API endpoint. You learn the shape once, write the destructure once, and every call gets clean local variables automatically.

Pay close attention to how array and object destructuring combine in the examples below — that mix is exactly what you'll encounter with real JSON payloads from GitHub's API, Stripe's API, or any other modern REST service.

Optional chaining with destructuring: Modern JavaScript (ES2020) allows optional chaining, but it doesn't work directly inside destructuring patterns. The pattern is: destructure from a defaulted object: const { data: { user } = {} } = response ?? {}

io/thecodeforge/destructuring/nestedDestructuring.jsJAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// ----- NESTED OBJECT DESTRUCTURING -----
// Simulating a response from a weather API
const weatherResponse = {
  status: 'ok',
  location: {
    city: 'San Francisco',
    country: 'US',
    coordinates: { lat: 37.7749, lng: -122.4194 }
  },
  current: {
    tempCelsius: 18,
    condition: 'Partly Cloudy',
    humidity: 72
  }
};

// Reach two levels deep — city and tempCelsius — in one declaration
const {
  location: { city, country },
  current: { tempCelsius, condition }
} = weatherResponse;

console.log(`${city}, ${country}: ${tempCelsius}°C — ${condition}`);
// → San Francisco, US: 18°C — Partly Cloudy

// NOTE: 'location' and 'current' are NOT available as variables here.
// They're pattern keys, not bindings. This is a common gotcha!
// console.log(location); // undefined (or the window.location in browsers!)


// ----- COMBINING OBJECT AND ARRAY DESTRUCTURING -----
// A GitHub-style API response for a repository's top contributors
const repoData = {
  repoName: 'open-ui',
  stars: 4821,
  topContributors: [
    { username: 'chloe_dev', commits: 342 },
    { username: 'marco_eng', commits: 289 },
    { username: 'priya_codes', commits: 201 }
  ]
};

// Destructure the array of objects — grab top 2 contributors
const {
  repoName,
  topContributors: [
    { username: firstContributor, commits: firstCommits },
    { username: secondContributor }
  ]
} = repoData;

console.log(`${repoName} — Top contributor: ${firstContributor} (${firstCommits} commits)`);
console.log(`Runner up: ${secondContributor}`);
// → open-ui — Top contributor: chloe_dev (342 commits)
// → Runner up: marco_eng


// ----- PRACTICAL ALTERNATIVE: BREAK DEEP NESTING INTO STEPS -----
// This is EASIER to read and debug than going 3+ levels in one line

const apiResponse = {
  data: {
    user: {
      account: {
        plan: 'pro',
        renewalDate: '2025-01-15'
      }
    }
  }
};

// Step 1: get to the relevant level
const { data: { user: { account } } } = apiResponse; // two levels is our limit

// Step 2: destructure the part we care about
const { plan, renewalDate } = account;

console.log(`Plan: ${plan}, Renews: ${renewalDate}`);
// → Plan: pro, Renews: 2025-01-15


// ----- DESTRUCTURING IN A LOOP — REAL WORLD TABLE RENDERING -----
const transactions = [
  { id: 'txn_001', amount: 49.99, currency: 'USD', status: 'settled' },
  { id: 'txn_002', amount: 120.00, currency: 'EUR', status: 'pending' },
  { id: 'txn_003', amount: 8.50, currency: 'USD', status: 'settled' }
];

// Destructure each item inline in the for...of loop
for (const { id, amount, currency, status } of transactions) {
  const flag = status === 'settled' ? '✅' : '⏳';
  console.log(`${flag} ${id}: ${amount} ${currency}`);
}
// → ✅ txn_001: 49.99 USD
// → ⏳ txn_002: 120 EUR
// → ✅ txn_003: 8.5 USD
Output
San Francisco, US: 18°C — Partly Cloudy
open-ui — Top contributor: chloe_dev (342 commits)
Runner up: marco_eng
Plan: pro, Renews: 2025-01-15
✅ txn_001: 49.99 USD
⏳ txn_002: 120 EUR
✅ txn_003: 8.5 USD
Nested keys are patterns, not variables
When you write const { location: { city } } = response, the word location is a pattern key — it tells the engine where to look, but it does NOT create a location variable. Only city is declared. In a browser, accidentally reading location afterwards silently gives you window.location instead of undefined, which causes bizarre bugs that are hard to trace. If you need both the nested object AND properties from it, declare them separately: const { location } = response; const { city } = location;
Production Insight
A React component had const { data: { user: { name } } } = this.state. When this.state.data was null (loading state), the entire component crashed because it couldn't destructure null. The fix: const { data: { user: { name } = {} } = {} } = this.state. The default empty objects at each level prevent the "cannot destructure of undefined" error.
Rule: When destructuring nested data that may be missing, add fallbacks at each level: { data: { user: { name } = {} } = {} }.
Key Takeaway
Nested destructuring past two levels is a readability trap — break it into two separate destructuring statements.
When destructuring potentially missing data, add fallback empty objects at each level: { data: { user: { name } = {} } = {} }.
Rule: Use nested destructuring sparingly. If it requires more than one line of documentation, it's too deep.
● Production incidentPOST-MORTEMseverity: high

The API Response That Destructured to Undefined

Symptom
User dashboard fails to load, completely blank screen. Console error: TypeError: Cannot destructure property 'balance' of 'undefined' as it is undefined. Only happens on Tuesday mornings between 2-4 AM. Error logs show the same stack trace across all users.
Assumption
The team assumed the API always returned an object with a balance property. They didn't consider that the endpoint might return an empty array, null, or an error object during maintenance windows. They also didn't realise destructuring undefined throws immediately.
Root cause
The code was const { balance, available } = getAccountData(); The function getAccountData() returned undefined during a database failover (every Tuesday maintenance). Destructuring undefined throws: 'Cannot destructure property 'balance' of 'undefined''. The surrounding try/catch didn't catch it because the error was in the destructuring assignment itself, not inside the function. The React component rendered before the API call completed, and the fallback logic (checking if accountData existed) was after the destructuring line — too late. Additionally, the API sometimes returned { data: null } or { data: [] } depending on the state. The destructuring assumed data was always an object.
Fix
1. Changed destructuring to provide a fallback for missing/empty data: const { balance = 0, available = 0 } = accountData ?? {}; The ?? {} ensures the right-hand side is never undefined when destructuring. 2. Validated the shape before destructuring: if (accountData && typeof accountData === 'object' && !Array.isArray(accountData)) { destructure } 3. For API boundaries, always validate that the response is an object before destructuring: ``javascript const data = response.data ?? {}; const { userId, email } = data; ` 4. Added a generic error boundary in React that catches destructuring errors and shows a fallback UI. 5. Updated the API spec to guarantee a consistent shape: always return { data: {...} } even during maintenance, with a status: 'degraded'` flag.
Key lesson
  • Destructuring undefined throws immediately. Always add a fallback: const { prop } = obj ?? {}.
  • API responses are unreliable across maintenance windows. Assume null/undefined/empty array are possible.
  • Destructuring doesn't belong inside render functions without null checks. Validate before destructuring.
  • Use optional chaining with destructuring: const { user: { name } = {} } = response; to avoid nested undefined errors.
  • Add a generic error boundary that catches destructuring crashes and shows a fallback UI, not a blank screen.
Production debug guideSymptom → Action mapping for common destructuring errors in production JavaScript.5 entries
Symptom · 01
TypeError: Cannot destructure property 'X' of 'undefined'
Fix
The source value is undefined. Add fallback: const { prop } = source ?? {}. For nested: const { data: { user } = {} } = response. Also check if the source is sometimes an array instead of object.
Symptom · 02
Default value not applied when property exists but is null
Fix
Destructuring defaults only fire on undefined. null passes through. If your API returns null for missing fields, add explicit null check: const { timeout = 3000 } = config ?? {} won't help — you need const timeout = config.timeout ?? 3000.
Symptom · 03
SyntaxError: Unexpected token at = { name } =
Fix
You destructured without a declaration keyword. { name } = user is invalid at statement level because { is parsed as a block. Fix: const { name } = user or wrap with parentheses: ({ name } = user). The latter is for reassigning existing variables.
Symptom · 04
Nested destructuring returns undefined but outer destructuring succeeded
Fix
You likely reached too deep. const { data: { user: { profile } } } = response fails if any intermediate property is missing. Use optional chaining with destructuring: const { data: { user: { profile } = {} } = {} } = response or break into steps.
Symptom · 05
Renaming doesn't work — variable is undefined
Fix
Check colon syntax: const { sourceKey: localName } = obj. Not const { sourceKey = localName }. The colon is renaming, equals is default. They can combine: const { sourceKey: localName = 'default' } = obj.
★ Destructuring Debug Cheat SheetFast diagnostics for destructuring errors in production JavaScript applications.
Cannot destructure property of undefined
Immediate action
Add fallback empty object before destructuring
Commands
console.log('source type:', typeof source, 'value:', source)
const safe = source ?? {}; const { prop } = safe;
Fix now
Replace const { prop } = source with const { prop } = source ?? {}
Default value not being applied (null instead of default)+
Immediate action
Check if property exists but is null, not undefined
Commands
console.log('value:', source.prop, 'type:', typeof source.prop)
const value = source.prop !== undefined ? source.prop : defaultValue;
Fix now
Replace default with explicit check: const val = source.prop ?? defaultValue
Syntax error at destructuring line — 'Unexpected token'+
Immediate action
Check if destructuring is missing declaration keyword
Commands
grep -n '^{' script.js | grep -v 'const\|let\|var'
node -c script.js 2>&1 | grep -E 'Unexpected token|line'
Fix now
Add const, let, or wrap with parentheses ({ prop } = obj)
Nested destructuring returns undefined for deep property+
Immediate action
Break the destructuring into multiple steps
Commands
console.log('response:', JSON.stringify(response, null, 2))
const { data } = response; const { user } = data ?? {}; const { name } = user ?? {};
Fix now
Replace deep destructuring with sequential steps: limit to 2 levels per statement
Renamed variable is undefined (colon vs equals confusion)+
Immediate action
Check colon syntax — are you accidentally using equals to rename?
Commands
grep -n '=.*:.*=' script.js | head -5
node -e "const { a: b = 1 } = { a: 2 }; console.log(b)" # should log 2
Fix now
Use colon for rename: { sourceKey: localName }. Use equals for default: { key = 'default' }.
Array vs Object Destructuring
AspectArray DestructuringObject Destructuring
Binding mechanismBy position (index order matters)By key name (order irrelevant)
Syntax delimitersSquare brackets [ ]Curly braces { }
Renaming valuesJust use any variable name you like (position determines which element)Use sourceKey: newName syntax
Skipping elementsLeave a gap with a comma , ,Simply don't mention the key
Default valuesconst [a = 10] = []const { a = 10 } = {}
Rest/collect remainingconst [first, ...rest] = arrconst { a, ...others } = obj
Best used whenReturning multiple values from a function (useState), working with tuplesConsuming API objects, config params, component props
Risk of breakingHigh — adding an element at the start shifts all positionsLow — order-independent, adding new keys doesn't break existing destructures
Error on undefined/nullTypeError: Cannot iterate — use ?? fallbackTypeError: Cannot destructure — use ?? {} fallback

Key takeaways

1
Array destructuring binds by position
adding or removing elements from the source breaks your bindings silently, so prefer object destructuring whenever the data source is under someone else's control.
2
Defaults only fire on undefined, not null
when consuming APIs that use null to mean 'intentionally absent', you need an explicit nullish check alongside your default value.
3
Destructuring in function parameters isn't just style
it creates a named-property contract that makes functions easier to extend, since new optional params with defaults can be added without changing any call sites.
4
Nested destructuring past two levels is a readability trap
break it into two separate destructuring statements to keep it debuggable and reviewable.
5
Always add fallback when destructuring from potentially missing data
const { prop } = source ?? {} prevents crashes from undefined/null sources.
6
Renaming and defaults combine as `{ sourceKey
localName = 'default' }` — colon first, then equals. The order matters and is a common source of syntax errors.

Common mistakes to avoid

5 patterns
×

Destructuring without a declaration keyword

Symptom
SyntaxError: Unexpected token '{' at statement level. JavaScript parses the opening { as a block, not a destructuring pattern.
Fix
Always use const, let, or var: const { name, age } = person;. If reassigning existing variables, wrap in parentheses: ({ name, age } = person);
×

Destructuring from null or undefined

Symptom
TypeError: Cannot destructure property 'prop' of 'undefined' at runtime. The source value is not an object.
Fix
Add fallback: const { prop } = source ?? {}. For nested: const { data: { user } = {} } = response ?? {}. Always treat API responses as potentially undefined.
×

Confusing renaming syntax with default values

Symptom
SyntaxError: Invalid destructuring assignment target or variable named 3000. Writing const { timeout: 3000 } = config tries to assign to variable named 3000.
Fix
Correct syntax separates renaming and defaults: const { timeout: connectionTimeout = 3000 } = config. Colon renames, equals sets default, in that exact order.
×

Assuming null becomes default value

Symptom
Default value not applied: const [count = 0] = [null] gives null, not 0. const { active = true } = { active: null } gives null.
Fix
Destructuring defaults only fire on undefined. If your data may be null, use explicit nullish coalescing: const count = arr[0] ?? 0; or const active = obj.active ?? true;
×

Going too deep with nested destructuring

Symptom
Code becomes unreadable: const { data: { user: { profile: { address: { city } } } } } = response. One missing property anywhere breaks everything.
Fix
Limit destructuring to 2 levels. Break into steps: const { data } = response; const { user } = data ?? {}; const { profile } = user ?? {}; const { address } = profile ?? {}; const { city } = address ?? {}; This is more lines but each line is verifiable.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What's the difference between `const { a } = obj` and `const { a: a } = ...
Q02SENIOR
If a function returns an array, what are the practical advantages of hav...
Q03SENIOR
What does this code print and why: `const { a: { b }, a } = { a: { b: 42...
Q01 of 03SENIOR

What's the difference between `const { a } = obj` and `const { a: a } = obj`, and can you show how renaming works when you want to avoid a variable name collision with an existing variable in scope?

ANSWER
const { a } = obj creates a variable named a and assigns obj.a to it. const { a: a } = obj does exactly the same thing — the colon syntax renames the property a to the variable name a. It's verbose but identical. Renaming is useful when you have an existing variable named a in scope. Example: ``javascript const userId = 'already_taken'; const { user_id: userId } = apiResponse; // Renames user_id to userId, but userId already exists // This would cause a redeclaration error (userId already declared) ` To avoid collision, rename to something else: const { user_id: apiUserId } = apiResponse;. Another pattern: When the API uses snake_case but your codebase uses camelCase: `javascript const { user_name, account_id, is_active } = apiResponse; // Destructuring with renaming: const { user_name: userName, account_id: accountId, is_active: isActive } = apiResponse; ``
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Can you use destructuring with a function's return value directly?
02
Does destructuring mutate the original array or object?
03
What happens if I destructure a key that doesn't exist on the object?
04
What's the difference between `...` rest in array and object destructuring?
05
How do I destructure a property inside an array inside an object?
🔥

That's Advanced JS. Mark it forged?

4 min read · try the examples if you haven't

Previous
ES6+ Features in JavaScript
7 / 27 · Advanced JS
Next
Spread and Rest Operators