Nullish Coalescing (??) vs OR (||): When to Use Which
Understand the crucial difference between ?? and ||. Learn why ?? was added to JavaScript and when it saves you from bugs.
The Problem with ||
The OR operator (||) returns the first "truthy" value. This is great for many cases, but it treats valid values like 0, "", and false as if they were missing.
``javascript
const count = 0;
const result = count || 10;
console.log(result); // 10 - but we wanted 0!
const message = ''; const text = message || 'Default'; console.log(text); // 'Default' - but we wanted ''!
const isEnabled = false;
const enabled = isEnabled || true;
console.log(enabled); // true - but we wanted false!
`
This is a common source of bugs!
Enter Nullish Coalescing (??)
The ?? operator only returns the right side if the left side is null or undefined.
`javascript
const count = 0;
const result = count ?? 10;
console.log(result); // 0 - correct!
const message = ''; const text = message ?? 'Default'; console.log(text); // '' - correct!
const isEnabled = false;
const enabled = isEnabled ?? true;
console.log(enabled); // false - correct!
`
The Key Difference
| Value | value \|\| default | value ?? default |
|-------|---------------------|-------------------|
| null | default | default |
| undefined | default | default |
| 0 | default | 0 |
| "" | default | "" |
| false | default | false |
| NaN | default | NaN |
|| returns right side for ANY falsy value: false, 0, "", null, undefined, NaN
?? returns right side ONLY for null and undefined
Real-World Examples
User Settings
`javascript
function getUserSettings(settings) {
// BAD: || overwrites valid settings
return {
volume: settings.volume || 50, // 0 becomes 50!
darkMode: settings.darkMode || false, // false becomes false (ok by accident)
username: settings.username || 'Guest' // '' becomes 'Guest'!
};
}
// GOOD: ?? preserves valid settings
function getUserSettings(settings) {
return {
volume: settings.volume ?? 50, // 0 stays 0
darkMode: settings.darkMode ?? false, // false stays false
username: settings.username ?? 'Guest' // '' stays ''
};
}
`
API Responses
`javascript
// API returns: { items: [], total: 0 }
const data = await fetch('/api/products').then(r => r.json());
// BAD const items = data.items || ['No items']; // [] becomes ['No items'] const total = data.total || 'Unknown'; // 0 becomes 'Unknown'
// GOOD
const items = data.items ?? ['No items']; // [] stays []
const total = data.total ?? 'Unknown'; // 0 stays 0
`
Function Parameters
`javascript
// BAD: Can't pass 0 or '' as valid arguments
function greet(name, times) {
const actualName = name || 'Guest';
const actualTimes = times || 1;
for (let i = 0; i < actualTimes; i++) { console.log('Hello, ' + actualName); } }
greet('', 0); // Logs "Hello, Guest" once - wrong!
// GOOD: 0 and '' are valid function greet(name, times) { const actualName = name ?? 'Guest'; const actualTimes = times ?? 1;
for (let i = 0; i < actualTimes; i++) { console.log('Hello, ' + actualName); } }
greet('', 0); // Logs nothing - correct!
`
Combining with Optional Chaining (?.)
?? works great with optional chaining:
`javascript
const user = {
profile: {
settings: {
theme: 'dark'
}
}
};
// Safe access with fallback const theme = user?.profile?.settings?.theme ?? 'light'; const fontSize = user?.profile?.settings?.fontSize ?? 16;
console.log(theme); // 'dark'
console.log(fontSize); // 16 (default, since property doesn't exist)
`
Short-Circuit Behavior
Both || and ?? short-circuit:
`javascript
// || stops at first truthy value
const a = 'hello' || expensiveFunction(); // expensiveFunction NOT called
// ?? stops at first non-nullish value
const b = 0 ?? expensiveFunction(); // expensiveFunction NOT called
const c = null ?? expensiveFunction(); // expensiveFunction IS called
`
Cannot Mix with && or || Without Parentheses
`javascript
// SyntaxError! Must use parentheses
const result = a || b ?? c;
// OK with parentheses
const result = (a || b) ?? c;
const result = a || (b ?? c);
`
When to Use Which
Use || when:
- You want to treat ALL falsy values as "missing"
- You're checking for "truthiness" in general`javascript
// Good use of ||
const displayName = user.nickname || user.firstName || 'Anonymous';
if (items.length || showEmpty) { /* render */ }
`
Use ?? when:
- You only want to replace null/undefined
- 0, "", false are valid values in your context`javascript
// Good use of ??
const port = config.port ?? 3000; // 0 is valid port
const message = input.trim() ?? ''; // '' is valid message
const count = response.count ?? -1; // 0 is valid count
`Nullish Assignment (??=)
ES2021 added a shorthand for nullish assignment:
`javascript
let user = { name: 'Alice' };// Old way
if (user.age === null || user.age === undefined) {
user.age = 25;
}
// With ??=
user.age ??= 25;
// Only assigns if null or undefined
user.name ??= 'Guest'; // Doesn't change - 'Alice' is not nullish
`Summary
-
|| returns right side for ANY falsy value
- ?? returns right side ONLY for null/undefined
- Use ?? when 0, "", false are valid values
- Use || when you want to treat all falsy values as missing
- Combine ?? with ?. for safe property access with defaults
- Use ??=` for conditional assignment