Nullish coalescing operator '??'

The nullish coalescing operator is written as two question marks: ??. Its whole job is to answer one question — “does this value actually exist?” — and hand you a fallback when it doesn’t.

Because ?? treats null and undefined as the same thing, this article needs a shorthand for “not one of those two.” When a value is neither null nor undefined, we’ll call it defined. That single word saves a lot of repetition.

Here’s the rule in full. For a ?? b:

  • if a is defined, the result is a,
  • if a is not defined, the result is b.

Put plainly: ?? gives you back the first operand when it holds a real value, and the second operand otherwise.

a ?? b
→ is a defined?
yes
result is
a
no (null / undefined)
result is
b
?? picks the left operand unless it's null or undefined, in which case it falls through to the right.

There’s nothing magical happening under the hood. ?? is a compact way to reach for the first defined value out of two candidates. You could already express the same idea with operators you know:

result = (a !== null && a !== undefined) ? a : b;

Read that once and ?? stops being mysterious: check a against both “empty” values, use it if it survives, fall back to b if it doesn’t. The operator just spares you from writing that mouthful every time.

Where it earns its keep: default values

The everyday use for ?? is supplying a fallback when a variable might not be set.

Say you want to greet a user by name, but drop back to "Anonymous" when there’s no name to show:

let user;

alert(user ?? "Anonymous"); // Anonymous (user is undefined)

Here user was declared but never assigned, so it holds undefined. That’s not defined, so ?? moves on to the fallback.

Give user an actual value and the fallback never runs:

let user = "Maya";

alert(user ?? "Anonymous"); // Maya (user is not null/undefined)

Type a name below and the greeting uses it directly. Clear the field and the value becomes an empty string — but notice that ?? still keeps "" (it’s a defined value), so the fallback only appears when you deliberately unset the name. Press “Unset name” to make it undefined and watch the fallback kick in.

interactive?? supplying a default

Chaining to find the first defined value

?? isn’t limited to two operands. Chain several together and JavaScript walks left to right, stopping at the first one that’s defined.

Imagine you’re building a label for a chat account and the person may have set any of preferredName, username, or handle — or none of them. You want to show whichever one exists first, and "Anonymous" only if all three are empty:

let preferredName = null;
let username = null;
let handle = "PixelWizard";

// shows the first defined value:
alert(preferredName ?? username ?? handle ?? "Anonymous"); // PixelWizard

The chain checks preferredName (null, skip), then username (null, skip), then handle ("PixelWizard" — defined, done). It never reaches "Anonymous".

preferredName
null
skip →
username
null
skip →
handle
“PixelWizard”
stop ✓
“Anonymous”
never reached
A ?? chain scans left to right and returns at the first defined operand; everything after it is ignored.

Toggle each field on or off below to control whether it holds a value or stays null. The chain always lands on the first one still switched on — flip them all off and only then does "Anonymous" show up.

interactiveWalking a ?? chain

Comparison with ||

You can build the same “first value that counts” behavior with the OR operator ||, which was covered back in the logical operators chapter. Swap ?? for || in the example above and the output is identical:

let preferredName = null;
let username = null;
let handle = "PixelWizard";

// shows the first truthy value:
alert(preferredName || username || handle || "Anonymous"); // PixelWizard

So why does ?? exist at all? History and a genuine annoyance.

|| has been in JavaScript since day one, and for years it was the default tool for supplying fallback values. But it has a sharp edge that bit people often enough that the language committee added ?? as a dedicated fix.

The difference is precise:

  • || returns the first truthy value.
  • ?? returns the first defined value.

That gap matters because || lumps together a whole family of values. To ||, the number 0, an empty string "", false, null, and undefined are all the same — they’re all falsy, so || skips right past them. But most of the time you only want a fallback when a value is genuinely missing, not when it happens to be zero or empty.

Here’s the classic trap, side by side:

let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0

Walk through both:

  • height || 100 asks “is height falsy?” — and 0 is falsy, so it discards it and returns 100.
  • height ?? 100 asks “is height null or undefined?” — and 0 is neither, so it keeps height as-is: 0.

A height of zero is a perfectly legitimate value. You almost never want to silently replace it with 100. This is exactly the situation ?? was built for.

height || 100
0 is falsy → skip
result: 100
height ?? 100
0 is defined → keep
result: 0
With height = 0, the two operators disagree because 0 is falsy but still 'defined'.

Pick a left-hand value and watch the two operators diverge. Whenever the value is falsy-but-defined — 0, "", or false|| throws it away while ?? keeps it. Only for null and undefined do the two agree.

interactive|| discards falsy, ?? discards only null/undefined

Precedence

?? sits at the same precedence level as ||. In the MDN precedence table they share a rank near the bottom.

The practical takeaway: ?? runs before assignment = and the ternary ?, but after the arithmetic operators like + and *. Since math binds tighter, a ?? mixed into an arithmetic expression can bind in a surprising order, so you’ll often need parentheses to force the grouping you mean.

let height = null;
let width = null;

// important: use parentheses
let area = (height ?? 100) * (width ?? 50);

alert(area); // 5000

Drop those parentheses and * grabs its neighbors first, which produces something you didn’t intend:

// without parentheses
let area = height ?? 100 * width ?? 50;

// ...actually groups like this (wrong!):
let area = height ?? (100 * width) ?? 50;

With height and width both null, 100 * width becomes 100 * null which is 0 — and the whole thing collapses to a value you never wanted. The fix is always the same: wrap each ?? fallback in its own parentheses when arithmetic is nearby.

Using ?? with && or ||

JavaScript flatly refuses to let you write ?? next to && or || without parentheses. This one is a hard syntax rule, not a style suggestion:

let x = 1 && 2 ?? 3; // Syntax error

The reasoning is defensive. When ?? was new, plenty of developers were migrating code from || to ??, and mixing the two in one expression makes the intended grouping genuinely ambiguous to a human reader. Rather than pick a silent default that might surprise you, the specification forces you to spell it out. It’s a debatable call, but the intent was to head off subtle bugs during the switch.

Adding parentheses satisfies the parser and makes your intent unmistakable:

let x = (1 && 2) ?? 3; // Works

alert(x); // 2

Summary

  • The nullish coalescing operator ?? gives you a short way to pull the first defined value (not null, not undefined) from a list of candidates. Its headline use is assigning defaults:

    // set height = 100 only if height is null or undefined
    height = height ?? 100;
  • The key contrast with ||: ?? only steps past null and undefined, while || steps past every falsy value — so 0 and "" survive ?? but not ||. Reach for ?? whenever zero, empty string, or false are valid values you want to keep.

  • ?? has very low precedence — just above ? and = — so wrap it in parentheses when it shares an expression with arithmetic.

  • Combining ?? with && or || without parentheses is a syntax error. Add the parentheses and it works.