These two operators (introduced in ES2020) solve very common pain points. Let’s look at each one.
Optional Chaining (?.)
Before optional chaining, accessing a deeply nested property was painful because we had to check every level to avoid “Cannot read property of undefined” errors.
const user = { address: { city: 'Mumbai' } };
// Old way — check every level
const city = user && user.address && user.address.city;
// With optional chaining — clean and short
const city2 = user?.address?.city; // 'Mumbai'
If any part in the chain is null or undefined, it short-circuits and returns undefined instead of throwing an error.
const user = {};
console.log(user?.address?.city); // undefined (no error!)
console.log(user?.address?.city?.toUpperCase()); // undefined
Works with methods and arrays too
Optional chaining isn’t just for properties — we can use it with method calls and array access.
const user = { greet: null, scores: [10, 20] };
// Method calls — won't throw if method doesn't exist
user.greet?.(); // undefined (greet is null, not called)
user.sayHello?.(); // undefined (doesn't exist, no error)
// Array access
user.scores?.[0]; // 10
user.friends?.[0]; // undefined (friends doesn't exist)
Nullish Coalescing (??)
The ?? operator returns the right-hand side only when the left-hand side is null or undefined. Not when it’s 0, "", or false — that’s the key difference from ||.
const count = 0;
// || treats 0 as falsy, so it falls through
console.log(count || 10); // 10 (not what we want!)
// ?? only checks for null/undefined
console.log(count ?? 10); // 0 (correct!)
The Big Difference: ?? vs ||
This comes up in interviews all the time. || returns the right side for any falsy value (0, "", false, null, undefined, NaN). ?? returns the right side only for null and undefined.
console.log(0 || 'default'); // 'default' (0 is falsy)
console.log(0 ?? 'default'); // 0
console.log('' || 'default'); // 'default' (empty string is falsy)
console.log('' ?? 'default'); // ''
console.log(false || 'default'); // 'default'
console.log(false ?? 'default'); // false
console.log(null || 'default'); // 'default'
console.log(null ?? 'default'); // 'default' (same here)
console.log(undefined || 'default'); // 'default'
console.log(undefined ?? 'default'); // 'default' (same here)
In simple language, use ?? when 0, "", or false are valid values that you want to keep. Use || when you want to fall through on any falsy value.
Using Them Together
Optional chaining and nullish coalescing pair really well together — one safely accesses the value, the other provides a fallback.
const user = { settings: { theme: null } };
// Safely access + provide a default
const theme = user?.settings?.theme ?? 'light';
console.log(theme); // 'light' (theme is null, so ?? kicks in)