Object Destructuring in JavaScript
Object destructuring in JavaScript is not just shorter dot access. It is a way to make the shape of your data visible at the exact place you use it. That is useful when the object is stable, and it is also where the sharp edges live: a misspelled property becomes undefined, a missing object can throw before your function body runs, and a default only helps for one kind of missing value.
Use destructuring when naming the needed properties makes the code easier to read. Keep normal dot access when pulling apart the object would hide a simpler story.
Contents
- Read properties into variables
- Rename while destructuring
- Use default values
- Destructure function parameters
- Collect rest properties
- Common missing-property bugs
- How this connects to practice
- Key Takeaways
Read properties into variables
Object destructuring reads properties by name:
const ticket = {
id: "BUG-17",
owner: "Nia",
priority: "high",
};
const { id, owner, priority } = ticket;
console.log(id); // "BUG-17"
console.log(owner); // "Nia"
console.log(priority); // "high"
That is the same data you would get with dot access:
const id = ticket.id;
const owner = ticket.owner;
const priority = ticket.priority;
The destructuring version is useful because it shows the object's required shape in one place. The variable names are not arbitrary in the basic form. id reads ticket.id, owner reads ticket.owner, and priority reads ticket.priority.
Property order does not matter:
const product = {
price: 49,
inStock: true,
name: "Keyboard",
};
const { name, price, inStock } = product;
console.log(`${name} costs $${price}`);
Object destructuring is key-based. It does not care that price appears before name in the object.
Rename while destructuring
Sometimes the property name is too generic for the local scope. Use : to read one property name into a different variable name:
const user = {
id: 42,
name: "Mira",
role: "admin",
};
const { id: userId, name: displayName } = user;
console.log(userId); // 42
console.log(displayName); // "Mira"
Read the syntax as "property name first, local variable name second":
{ propertyName: localVariableName }
The colon does not create both names. In this example, userId exists as a variable. id does not:
const { id: userId } = user;
console.log(userId); // 42
console.log(id);
// ReferenceError: id is not defined
That is the first common renaming bug. The second is accidentally treating the new name as the property name:
const { userId } = user;
console.log(userId); // undefined
JavaScript looked for user.userId. It did not find it. If the object property is still named id, you need { id: userId }.
Use default values
A destructuring default fills in when the property value is undefined:
const ticket = {
id: "DOC-2",
owner: "Sam",
};
const { id, owner, priority = "normal" } = ticket;
console.log(priority); // "normal"
This is a good fit for optional properties from API records, form state, and config objects:
function formatTicket(ticket) {
const { id, owner, priority = "normal" } = ticket;
return `${id} assigned to ${owner} (${priority})`;
}
console.log(formatTicket({ id: "BUG-17", owner: "Nia", priority: "high" }));
// "BUG-17 assigned to Nia (high)"
console.log(formatTicket({ id: "DOC-2", owner: "Sam" }));
// "DOC-2 assigned to Sam (normal)"
Defaults do not replace every falsy value:
const settings = {
notifications: false,
pageSize: 0,
label: "",
theme: null,
};
const {
notifications = true,
pageSize = 20,
label = "Untitled",
theme = "system",
language = "en",
} = settings;
console.log(notifications); // false
console.log(pageSize); // 0
console.log(label); // ""
console.log(theme); // null
console.log(language); // "en"
Only language used its default because only language was missing. false, 0, "", and null were real values in the object.
For the same rule in function signatures, read Default Parameters in JavaScript.
Destructure function parameters
Object destructuring can happen directly in a function parameter list:
function formatProduct({ name, price, inStock }) {
const status = inStock ? "In stock" : "Out of stock";
return `${name} costs $${price} - ${status}`;
}
console.log(formatProduct({ name: "Keyboard", price: 49, inStock: true }));
// "Keyboard costs $49 - In stock"
This pattern tells the reader which properties the function uses before they read the body. It is especially helpful for option objects:
function createButton(label, { variant = "primary", disabled = false } = {}) {
return {
label,
variant,
disabled,
};
}
console.log(createButton("Save"));
// { label: "Save", variant: "primary", disabled: false }
console.log(createButton("Delete", { variant: "danger" }));
// { label: "Delete", variant: "danger", disabled: false }
There are two defaults doing different jobs:
= {}after the parameter handles a missing options object.variant = "primary"anddisabled = falsehandle missing properties inside that object.
The difference matters. Without the whole-object default, calling createButton("Save") would try to destructure undefined and throw.
For the larger function design side, see Parameters and Arguments in JavaScript.
Collect rest properties
Object rest collects the properties that were not already destructured:
const user = {
id: 1,
name: "Mira",
email: "mira@example.com",
passwordHash: "secret",
internalNotes: "vip",
};
const { passwordHash, internalNotes, ...publicUser } = user;
console.log(publicUser);
// { id: 1, name: "Mira", email: "mira@example.com" }
This is a clean way to omit a few known properties while keeping whatever else belongs in the object.
Rest must come last:
const { ...publicUser, passwordHash } = user;
// SyntaxError: Rest element must be last element
Object rest creates a new top-level object. It does not deep-clone nested objects:
const account = {
id: 1,
profile: {
timezone: "UTC",
},
passwordHash: "secret",
};
const { passwordHash, ...safeAccount } = account;
console.log(safeAccount === account); // false
console.log(safeAccount.profile === account.profile); // true
That same shallow-copy rule appears with object spread. Read JavaScript Spread Operator when you need to copy, merge, or update object data without mutating the original.
Common missing-property bugs
Destructuring is direct. It does not validate the object for you. That is where most bugs come from.
A missing property becomes undefined
This code looks tidy until a record omits priority:
function loudPriority(ticket) {
const { priority } = ticket;
return priority.toUpperCase();
}
loudPriority({ id: "DOC-2" });
// TypeError: Cannot read properties of undefined (reading 'toUpperCase')
Use a default when the property is optional:
function loudPriority(ticket) {
const { priority = "normal" } = ticket;
return priority.toUpperCase();
}
console.log(loudPriority({ id: "DOC-2" })); // "NORMAL"
If the property is required, a default may hide a bad caller. In that case, check explicitly:
function loudPriority(ticket) {
const { priority } = ticket;
if (priority === undefined) {
throw new Error("priority is required");
}
return priority.toUpperCase();
}
A typo also becomes undefined
JavaScript will not warn you about a misspelled property name in plain JavaScript:
const profile = {
username: "mira",
};
const { userName } = profile;
console.log(userName); // undefined
The object has username, not userName. Destructuring matched the name you wrote. It did not guess the one you meant.
Destructuring null or undefined throws
You can read a missing property from an object and get undefined. You cannot destructure from a missing object:
function getTheme(settings) {
const { theme = "system" } = settings;
return theme;
}
getTheme();
// TypeError: Cannot destructure property 'theme' of 'settings' as it is undefined.
If a missing object should mean "use defaults", give the parameter a default:
function getTheme(settings = {}) {
const { theme = "system" } = settings;
return theme;
}
console.log(getTheme()); // "system"
That still does not protect against null, because null is a provided value:
getTheme(null);
// TypeError: Cannot destructure property 'theme' of 'settings' as it is null.
Normalize with ?? {} when both null and undefined should fall back to an empty object:
function getTheme(settings) {
const safeSettings = settings ?? {};
const { theme = "system" } = safeSettings;
return theme;
}
console.log(getTheme(null)); // "system"
Nested destructuring needs parent defaults
Nested destructuring is compact, but the parent object must exist:
const user = {
name: "Nia",
};
const {
profile: { timezone },
} = user;
// TypeError: Cannot read properties of undefined (reading 'timezone')
Give the parent pattern a default if the parent property is optional:
const {
profile: { timezone = "UTC" } = {},
} = user;
console.log(timezone); // "UTC"
This is correct, but it is dense. When the nested shape has real business meaning, a couple of separate lines can be kinder to the next reader:
const profile = user.profile ?? {};
const { timezone = "UTC" } = profile;
Readable code is still allowed. Very controversial, apparently.
How this connects to practice
The Working with Data: Destructuring and Spread lesson teaches object destructuring alongside array destructuring, rest, and spread. The article you are reading is the slower reference version of the object side.
Use these practice links when you want the syntax to stick:
- Write Object Destructuring for
working-with-data-destructuring-08 - Destructuring in Function Parameters for
working-with-data-destructuring-09 - Destructuring Defaults for optional properties with defaults
- Object Rest for omitting private fields without mutation
For related reading, use JavaScript Spread Operator for copying and merging objects, Default Parameters in JavaScript for whole-argument fallbacks, and Parameters and Arguments in JavaScript for the function-call model behind destructured parameters.
Key Takeaways
- Object destructuring reads by property name, not property order.
- Rename with
{ originalName: localName }, and remember that only the local name becomes a variable. - Destructuring defaults apply to
undefined, not tonull,false,0, or"". - Parameter destructuring is great for option objects, but a missing object needs a whole-parameter default or
?? {}. - Object rest is useful for omitting known fields, but it only creates a new top-level object.