Have you ever logged a value, expected 0, and instead seen your “default” kick in and silently change the result? That was the exact moment the nullish coalescing operator ?? clicked for many JavaScript developers: it gives you defaults only when a value is truly missing, not just falsy like 0 or " ".
Why ?? solved a real bug for me
On one project, there was a pagination component where currentPage could legitimately be 0 for the first page. The code looked innocent:
const page = currentPage || 1;
Most of the time it worked—but whenever currentPage was 0, the expression treated it as “no value” and silently switched to 1. Users kept getting redirected to page 1 even though the API clearly returned 0. That bug cost a surprisingly long debugging session.
The fix was one tiny change:
const page = currentPage ?? 1;
Suddenly, 0 stayed 0. Only null or undefined triggered the default. That was the day ?? went from “new operator” to “must‑have tool” in the mental toolbox for many developers.
What the nullish coalescing operator actually does
The nullish coalescing operator ?? looks just like || at a glance, but the rule it follows is different:
- value ?? fallback
- otherwise returns fallback
In other words:
This makes it ideal when you want to distinguish between “not provided at all” and “provided, but falsy on purpose”.
- Treat only null and undefined as “missing”
- Keep 0, false, " ", and NaN as valid values
Quick comparison: || vs ??
Consider this table of behaviors for the same value and different operators:
// Logical OR
console.log(0 || 10); // 10
console.log("" || "default"); // "default"
console.log(false || true); // true
console.log(null || "fallback"); // "fallback"
console.log(undefined || "fallback"); // "fallback"
// Nullish coalescing
console.log(0 ?? 10); // 0
console.log("" ?? "default"); // ""
console.log(false ?? true); // false
console.log(null ?? "fallback"); // "fallback"
console.log(undefined ?? "fallback"); // "fallback"
So the mental shortcut is:
- Use || when you consider all falsy values as “empty” and want a default for them.
- Use ?? when 0, " ", and false are legitimate values you want to preserve.
Everyday use cases where ?? shines
1. Setting default values without breaking 0 or " "
Imagine a user preferences object:
const settings = {
theme: "dark",
itemsPerPage: 0, // 0 means "show all"
nickname: "", // empty string is allowed
};
If you write:
const itemsPerPage = settings.itemsPerPage || 10;
const nickname = settings.nickname || "Guest";
you accidentally overwrite legitimate values:
With ??:
- itemsPerPage becomes 10 instead of 0
- nickname becomes "Guest" instead of ""
const itemsPerPage = settings.itemsPerPage ?? 10; // stays 0
const nickname = settings.nickname ?? "Guest"; // stays ""
The defaults now only apply if the property is missing or explicitly null.
2. Optional function arguments
Sometimes you want to allow callers to deliberately pass 0 or false as values while still having a default when they omit the argument:
function createTimer(delayMs) {
const delay = delayMs ?? 1000; // default only if undefined/null
console.log(`Timer delay: ${delay}ms`);
}
createTimer(); // 1000
createTimer(0); // 0 (allowed)
createTimer(null); // 1000
This avoids the common trap of using delayMs || 1000, which would treat 0 as “no delay”.
3. Configuration and environment variables
In configuration files or environment variables, it is common to have explicit false or 0 values. Using ?? lets configuration stay in control:
const config = {
logLevel: 0, // 0 = only errors
enableCache: false, // explicitly disabled
};
const logLevel = config.logLevel ?? 2; // 0 is respected
const enableCache = config.enableCache ?? true; // false is respected
Only if logLevel or enableCache are missing or null will the defaults kick in.
4. Dealing with messy API data
APIs sometimes return null when data is not present. ?? helps you convert those to sane defaults while still honoring valid zero‑like values:
const user = {
name: "Jay",
age: null, // unknown age
score: 0, // score 0 is valid
};
const age = user.age ?? "N/A"; // "N/A"
const score = user.score ?? 100; // 0 (kept)
If the backend later starts sending undefined instead of null, the same logic still works because ?? treats both as “missing”.
5. Combining with optional chaining
The combination of optional chaining ?. and ?? is extremely useful for deep data structures:
const city = user.profile?.address?.city ?? "Unknown";
This single line says:
This pattern is perfect when dealing with deeply nested API responses or form data where some branches might not exist.
- If user.profile or address is missing, do not crash—just yield undefined.
- If the final city is null or undefined, default to "Unknown".
- But if city is an empty string, keep it as is.
When should you actually reach for '??' ?
A simple rule of thumb:
- If your domain treats 0, false, or "" as valid user choices → prefer ??.
- If you really mean “anything falsy should fall back” → || is fine.
