Back to Blog
Tips2025-01-01

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