Skip to content
JavaScript js objects 4 min read

Optional Chaining

Reaching into a nested object is one of the most common sources of runtime crashes in JavaScript. The moment one link in the chain is null or undefined, accessing a property on it throws TypeError: Cannot read properties of undefined. Optional chaining (?.), introduced in ES2020, lets you walk through deep or uncertain structures and short-circuit safely to undefined instead of blowing up. It is the cleanest way to handle data that might not be fully populated — API responses, optional config, partial form state.

The problem it solves

Before optional chaining you had to guard every level of access manually, which produced noisy, repetitive code:

const user = { profile: { name: "Ada" } };

// Without optional chaining
const city =
  user && user.address && user.address.city
    ? user.address.city
    : undefined;

console.log(city);

Output:

undefined

If you skip those guards and user.address is missing, the access throws:

const user = { profile: { name: "Ada" } };
console.log(user.address.city); // TypeError: Cannot read properties of undefined (reading 'city')

Accessing properties safely

The ?. operator checks the value on its left. If that value is null or undefined, the whole expression short-circuits and evaluates to undefined. Otherwise it proceeds with the property access as normal.

const user = { profile: { name: "Ada" } };

console.log(user.profile?.name);   // "Ada"
console.log(user.address?.city);   // undefined (no throw)
console.log(user.address?.city?.toUpperCase()); // undefined

Output:

Ada
undefined
undefined

Note that ?. only protects against null and undefined — not against missing values further down a non-nullish object. {}.profile?.name is fine, but ({ profile: 5 }).profile.name still throws because 5 is not nullish.

Calling methods that may not exist

Use ?.() to call a function only if it is actually defined. This is ideal for optional callbacks or feature-detecting APIs.

function notify(onDone) {
  // Call the callback only if one was passed
  onDone?.("finished");
}

notify();                          // no error, nothing happens
notify((msg) => console.log(msg)); // logs the message

Output:

finished

Be careful: obj.method?.() guards against method being null/undefined, but if method exists and is not a function (e.g. a string), the call still throws TypeError: ... is not a function.

Safe array and dynamic access

The bracket form ?.[] applies the same short-circuit to indexed or computed access. It is handy for arrays that might be absent and for dynamic keys.

const data = { tags: ["js", "ts"] };

console.log(data.tags?.[0]);       // "js"
console.log(data.tags?.[99]);      // undefined (in-bounds check not needed)
console.log(data.missing?.[0]);    // undefined (no throw)

const key = "tags";
console.log(data?.[key]?.length);  // 2

Output:

js
undefined
undefined
2

Short-circuiting behavior

When ?. short-circuits, evaluation stops immediately — the rest of the chain, including function calls and bracket lookups, is skipped entirely. This matters when later parts of the expression have side effects.

let called = false;
const getIndex = () => {
  called = true;
  return 0;
};

const obj = null;
const result = obj?.list[getIndex()];

console.log(result); // undefined
console.log(called); // false — getIndex was never invoked

Output:

undefined
false

Combining with nullish coalescing for defaults

?. resolves a missing path to undefined, which pairs naturally with the nullish coalescing operator ?? to supply a fallback. Unlike ||, ?? only substitutes for null/undefined, so legitimate falsy values like 0 or "" are preserved.

const settings = { theme: { mode: "dark" }, fontSize: 0 };

const mode = settings.theme?.mode ?? "light";
const lang = settings.locale?.language ?? "en";
const size = settings.fontSize ?? 14;

console.log(mode, lang, size);

Output:

dark en 0

Here fontSize of 0 is kept because ?? ignores falsiness — using || 14 would have wrongly replaced it.

Operator reference

SyntaxUse forReturns when left side is nullish
a?.bProperty accessundefined
a?.[expr]Computed / array accessundefined
a?.()Function / method callundefined
a ?? bDefault valueb

Optional chaining cannot be used on the left side of an assignment. obj?.prop = 1 is a syntax error — ?. is for reading, not writing.

Best Practices

  • Reach for ?. when a value is genuinely optional; do not sprinkle it everywhere, as it can mask real bugs by silently swallowing undefined.
  • Pair ?. with ?? to turn a missing path into a sensible default in one expression.
  • Prefer ?? over || for defaults so that valid 0, "", and false values survive.
  • Use ?.() for optional callbacks and feature detection instead of typeof fn === "function" checks.
  • Remember ?. guards only the link directly to its left — it is not a blanket “make this whole expression safe” tool.
  • Keep chains readable; if you need a?.b?.c?.d?.e, consider validating or destructuring the data shape earlier instead.
Last updated June 1, 2026
Was this helpful?