Menu
Try in Playground

JavaScript Optional Chaining (?.): Safe Access to Nested Properties

How the ?. operator lets you reach into nested objects, arrays, and methods without blowing up on null or undefined.

The Problem: Nested Access Is Fragile

Reaching into a nested object is fine — right up until one of the levels isn't there:

index.js
Output
Click Run to see the output here.

user.address is undefined, and reading .city off undefined throws TypeError: Cannot read properties of undefined. Real-world data — API responses, parsed JSON, DOM queries — is full of fields that might or might not exist, and writing defensive checks for each level gets noisy fast:

const city = user && user.address && user.address.city;

Readable-ish with two levels. Painful at four. The ?. operator is the cleaner fix.

?. Short-Circuits on null and undefined

Optional chaining reads a property only if the value before it isn't null or undefined. If it is, the whole expression evaluates to undefined and stops.

index.js
Output
Click Run to see the output here.

Line one reads name normally. Line two hits user.address, sees undefined, and bails out — the rest of the chain never runs. Line three would throw without ?. because .toUpperCase() would be called on undefined; with ?., the whole thing quietly evaluates to undefined.

The mental model: ?. means "if what's before me is null or undefined, give up and return undefined — otherwise keep going."

It Works on Arrays and Function Calls Too

Three syntaxes, same idea:

index.js
Output
Click Run to see the output here.
  • ?.[...] for array or dynamic property access.
  • ?.() for calling something that may or may not be a function.
  • ?.name for regular property access.

All three short-circuit the same way. user?.notAMethod?.() doesn't throw even though notAMethod doesn't exist — the second ?. sees undefined and stops.

This is especially handy for optional callbacks:

index.js
Output
Click Run to see the output here.

No more if (typeof onDone === "function") guards before every invocation.

Only null and undefined Trigger the Short-Circuit

This is the detail that trips people up. ?. only cares about nullish values. Other falsy values — 0, "", false, NaN — are perfectly valid objects to chain on (well, to the extent that autoboxing lets you):

index.js
Output
Click Run to see the output here.

data.count is 0, which is falsy but not nullish, so ?.toFixed(2) runs on it and returns "0.00". Contrast that with &&:

index.js
Output
Click Run to see the output here.

The && version returns 0 because data.count is falsy and short-circuits. The ?. version returns "0.00" because 0 isn't nullish. If you actually want to stop on 0, && is right. If you only want to stop on "the value is missing," ?. is what you want.

Where the ? Goes Matters

?. protects the value before it — not after. So place it on the level that might be missing:

index.js
Output
Click Run to see the output here.

All three work here because nothing's actually missing. But if config.server could be undefined, you need config.server?.host — a ?. before server wouldn't help, because the problem is reading .host off a missing server.

A good rule: put ?. on each level where the value to its left might genuinely be nullish. Scattering ?. on every dot "just in case" hides bugs and makes the code noisier.

Assigning Through ?. Doesn't Work

Optional chaining is read-only. You can't use it on the left of an assignment:

user?.address?.city = "Paris";   // SyntaxError

That makes sense if you think about it — what would it mean to assign to a property of undefined? If you need to set a value only when the parent exists, write it out:

index.js
Output
Click Run to see the output here.

When to Reach for It — and When Not To

?. shines when a value is legitimately optional:

  • API responses where fields may or may not be included.
  • DOM lookups: document.querySelector(".banner")?.remove().
  • Callbacks that might not be passed: options.onError?.(err).
  • Chained library calls where intermediate results can be null.

It's the wrong tool when the value should always exist. Sprinkling ?. to silence errors in that case converts a loud, findable bug ("TypeError on line 42") into a silent one (a variable mysteriously undefined three functions away). When something must be there, let it throw — the stack trace is doing you a favor.

A Realistic Example

Pulling a value out of a possibly-partial API response:

index.js
Output
Click Run to see the output here.

Each ?. handles one level of uncertainty. avatarUrl ends up undefined cleanly. onClick?.() calls the handler if it happens to be there.

You'll notice the ?? on the displayName line — that's the other half of this pattern. ?. gives you undefined when something's missing; ?? lets you substitute a default without tripping on legitimate falsy values like 0 or "".

Next: Nullish Coalescing

?. and ?? are the same idea applied to different problems: both treat null and undefined as "missing" and leave other falsy values alone. Next up, how ?? gives you sane default values — and why || has been quietly getting that wrong for years.

Frequently Asked Questions

What does ?. do in JavaScript?

The ?. operator accesses a property, array index, or method only if the value before it isn't null or undefined. If it is, the whole expression short-circuits and evaluates to undefined instead of throwing. user?.address?.city won't crash when user or address is missing.

When should you use optional chaining in JavaScript?

Use ?. when a value is legitimately allowed to be missing — API responses with optional fields, DOM lookups that might not find a node, callbacks that may or may not be passed in. Don't use it to paper over bugs where a value should always exist; in those cases a missing value is real information you want to see.

What's the difference between ?. and &&?

They produce the same result in most cases, but ?. only short-circuits on null or undefined, while && short-circuits on any falsy value — including 0, '', and false. obj && obj.count returns 0 when count is 0; obj?.count returns 0 too, but the chain stops cleanly at nullish values only.

Can you use optional chaining with arrays and function calls?

Yes. arr?.[0] safely reads an array index, and fn?.() calls a function only if it exists. Both follow the same rule: if the value before ?. is null or undefined, the expression is undefined and nothing else runs.

Learn to code with Coddy

GET STARTED