Optional Chaining
Access nested values safely. Returns undefined instead of throwing errors.
Property Access
Use ?. to safely access nested properties.
// The problem:
const user = { name: "Alice", address: null };
user.address.city; // ❌ TypeError: Cannot read properties of null
// Old fix — verbose checking:
const city = user && user.address && user.address.city;
// Optional chaining — clean and safe:
const city = user?.address?.city; // undefined (no error!)
// How it works:
// ?. checks if the left side is null/undefined
// If yes → returns undefined immediately
// If no → continues to the next property
const data = {
user: {
profile: { avatar: "pic.jpg" }
}
};
data?.user?.profile?.avatar; // "pic.jpg"
data?.user?.settings?.theme; // undefined (settings doesn't exist)
data?.missing?.deep?.value; // undefined (short-circuits at missing)Method Calls
Safely call methods that might not exist.
// Call a method only if it exists:
const user = {
name: "Alice",
greet() { return "Hello!"; }
};
user.greet?.(); // "Hello!"
user.goodbye?.(); // undefined (doesn't throw)
// Useful with optional callbacks:
function fetchData(url, onSuccess, onError) {
fetch(url)
.then(res => res.json())
.then(data => onSuccess?.(data)) // call only if provided
.catch(err => onError?.(err)); // call only if provided
}
// Without optional chaining:
// if (onSuccess) onSuccess(data);
// With: onSuccess?.(data);
// On DOM elements:
document.querySelector("#modal")?.remove();
// Removes if found, does nothing if nullDynamic Properties
Works with bracket notation too.
// Optional bracket access:
const data = { users: ["Alice", "Bob"] };
data?.users?.[0]; // "Alice"
data?.admins?.[0]; // undefined (admins doesn't exist)
// With variables:
const key = "email";
user?.[key]; // user.email if user exists, else undefined
// Array access:
const first = arr?.[0]; // safe first element
const last = arr?.[arr.length - 1]; // safe last element
// Combining all forms:
const result = api
?.response
?.data
?.items?.[0]
?.getName?.();
// Each ?. checks null/undefined at that stepShort-Circuiting
Evaluation stops at the first null/undefined.
// Once ?. hits null/undefined, the ENTIRE chain returns undefined: const obj = null; obj?.a.b.c; // undefined // Does NOT throw even though .a.b.c would normally error // ?. short-circuits — everything after is skipped // But be careful — only the chain after ?. is protected: const x = null; x.a?.b; // ❌ TypeError! (x.a throws before ?. is reached) x?.a?.b; // ✓ undefined (safe)
Once optional chaining encounters null/undefined, the entire rest of the expression evaluates to undefined.
1 / 2
Practical Uses
Real-world patterns with optional chaining.
// 1. API response handling:
const userName = response?.data?.user?.name ?? "Unknown";
const avatar = response?.data?.user?.avatar?.url;
// 2. Config with nested optional values:
const port = config?.server?.port ?? 3000;
const debug = config?.features?.debug ?? false;
// 3. DOM traversal:
const text = document.querySelector(".title")?.textContent;
const parent = element?.parentElement?.closest(".container");
// 4. Event handling:
element?.addEventListener("click", handler);
event?.target?.closest("button")?.dataset?.action;
// 5. Map/WeakMap access:
const cached = cache?.get(key)?.value;
// 6. JSON parsing safety:
const parsed = JSON.parse(text);
const nested = parsed?.results?.[0]?.metadata?.tags ?? [];
// 7. Combine with nullish coalescing (??):
const theme = user?.preferences?.theme ?? "system";
const items = cart?.items?.length ?? 0;FAQ
Common questions about optional chaining.