Nullish Coalescing
Learn how to provide fallback values cleanly using the nullish coalescing operator in JavaScript.
Nullish Coalescing
We already covered nullish coalescing in the Short Circuit Operators topic under Control Flow. You know what ?? does and why it exists.
This topic is short — it focuses specifically on how ?? is used with objects, and how it pairs with optional chaining to handle missing data cleanly.
Quick Recap
?? returns the right side only when the left side is null or undefined — not for any other falsy value like 0, false, or "".
console.log(null ?? "default"); // default
console.log(undefined ?? "default"); // default
console.log(0 ?? "default"); // 0 — 0 is valid, kept
console.log("" ?? "default"); // "" — empty string is valid, kept
console.log(false ?? "default"); // false — valid, keptThis makes it far more precise than || for fallback values on object properties.
Why It Matters With Objects
Object properties are often optional — some users have a bio, some do not. Some products have a discount, some have a price of 0. Using || for these fallbacks causes bugs because it treats valid falsy values as missing.
const product = {
name: "Budget Cable",
price: 0, // genuinely free or zero cost
inStock: false, // genuinely not in stock
description: "" // genuinely empty
};
// ❌ || treats 0, false, "" as missing — wrong results
console.log(product.price || "Price not set"); // "Price not set" — wrong, 0 is valid
console.log(product.inStock || "Status unknown"); // "Status unknown" — wrong, false is valid
console.log(product.description || "No description"); // "No description" — wrong, "" might be intentional
// ✅ ?? only falls back for null and undefined
console.log(product.price ?? "Price not set"); // 0 — correct
console.log(product.inStock ?? "Status unknown"); // false — correct
console.log(product.description ?? "No description"); // "" — correctPairing With Optional Chaining
This is the most powerful combination in modern JavaScript. Optional chaining accesses the property safely — nullish coalescing provides the fallback if it is missing.
object?.property ?? "fallback"const user = {
name: "Ali",
profile: {
bio: null,
website: "ali.dev"
}
};
console.log(user?.profile?.bio ?? "No bio added"); // No bio added — bio is null
console.log(user?.profile?.website ?? "No website"); // ali.dev — exists
console.log(user?.profile?.twitter ?? "No twitter"); // No twitter — property missing
console.log(user?.contact?.phone ?? "No phone"); // No phone — contact missingEvery access is safe and every missing value gets a meaningful fallback — no errors, no empty output.
With Dynamic Object Data
When you build UI from data — like a user profile page or a product listing — some fields will always be missing for some records. ?. and ?? together handle this gracefully.
const profiles = [
{
username: "ali_dev",
stats: { posts: 42, followers: 1200 },
location: "Lahore"
},
{
username: "sara_design",
stats: { posts: 18, followers: null },
// no location
},
{
username: "zara_writes"
// no stats, no location
}
];
for (const profile of profiles) {
const posts = profile?.stats?.posts ?? 0;
const followers = profile?.stats?.followers ?? 0;
const location = profile?.location ?? "Location not set";
console.log(`@${profile.username}`);
console.log(` Posts: ${posts} | Followers: ${followers}`);
console.log(` Location: ${location}`);
console.log("---");
}
// @ali_dev
// Posts: 42 | Followers: 1200
// Location: Lahore
// ---
// @sara_design
// Posts: 18 | Followers: 0
// Location: Location not set
// ---
// @zara_writes
// Posts: 0 | Followers: 0
// Location: Location not set
// ---Nullish Assignment ??=
ES2021 added a shorthand — nullish assignment. It assigns a value only if the current value is null or undefined.
let username = null;
username ??= "Guest";
console.log(username); // Guest — was null, assigned
let theme = "dark";
theme ??= "light";
console.log(theme); // dark — was not null/undefined, keptThis is equivalent to:
username = username ?? "Guest";Real use — setting defaults on an object only for missing properties:
const settings = {
theme: "dark",
language: null,
fontSize: undefined
};
settings.theme ??= "light"; // already "dark" — not changed
settings.language ??= "en"; // was null — set to "en"
settings.fontSize ??= 14; // was undefined — set to 14
console.log(settings);
// { theme: "dark", language: "en", fontSize: 14 }?? vs || vs ?. — Knowing Which to Use
These three are often used together. Here is a clear breakdown of what each one does:
| Operator | Purpose | Triggers fallback on |
|---|---|---|
|| | Fallback for any falsy value | false, 0, "", null, undefined, NaN |
?? | Fallback for missing values only | null, undefined |
?. | Safe property access | Stops if null or undefined |
const user = { score: 0, name: "" };
// || — wrong for scores and empty strings
console.log(user.score || "No score"); // "No score" — wrong, 0 is valid
console.log(user.name || "Anonymous"); // "Anonymous" — maybe wrong, "" might be valid
// ?? — correct
console.log(user.score ?? "No score"); // 0 — correct
console.log(user.name ?? "Anonymous"); // "" — correct, name exists
// ?. — safe access
console.log(user?.profile?.avatar ?? "No avatar"); // "No avatar" — safe and cleanThe combination of ?. and ?? is now the standard way to handle optional object data in modern JavaScript. You will see it everywhere — in React components, API handlers, and utility functions.
A Real Example — Product Page
function renderProduct(product) {
const name = product?.name ?? "Unnamed Product";
const price = product?.price ?? "Price not available";
const discount = product?.discount ?? 0;
const rating = product?.rating ?? "No ratings yet";
const description = product?.details?.desc ?? "No description available";
const stock = product?.inventory?.count ?? 0;
const stockLabel = stock > 0 ? `${stock} in stock` : "Out of stock";
console.log(`${name}`);
console.log(`Price: Rs. ${price}${discount ? ` (${discount}% off)` : ""}`);
console.log(`Rating: ${rating}`);
console.log(`${description}`);
console.log(`${stockLabel}`);
console.log("---");
}
renderProduct({
name: "Mechanical Keyboard",
price: 8500,
discount: 10,
rating: 4.5,
details: { desc: "RGB backlit, tactile switches" },
inventory: { count: 15 }
});
renderProduct({
name: "Old Mouse",
price: 0,
inventory: { count: 0 }
});
renderProduct(null);
// Mechanical Keyboard
// Price: Rs. 8500 (10% off)
// Rating: 4.5
// RGB backlit, tactile switches
// 15 in stock
// ---
// Old Mouse
// Price: Rs. 0
// Rating: No ratings yet
// No description available
// Out of stock
// ---
// Unnamed Product
// Price: Rs. Price not available
// Rating: No ratings yet
// No description available
// Out of stock
// ---All three cases handled — complete data, partial data, and completely missing data — cleanly and without a single if statement.
Summary
??returns the right side only when the left isnullorundefined- Use
??instead of||when0,false, or""are valid values - Pair with
?.for the safest and cleanest way to handle optional object data —obj?.prop ?? "fallback" ??=assigns a value only if the current one isnullorundefined||fallback — any falsy value triggers it??fallback — onlynullandundefinedtrigger it?.access — stops the chain safely ifnullorundefined