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 null

Dynamic 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 step

Short-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.