Ternary Operator
The conditional operator — condition ? a : b — is the only operator in JavaScript that takes three operands, which is why it's also called the ternary operator. It's a compact way to pick between two values based on a test, and the most common shortcut you'll write after if/else.
The shape
const result = condition ? valueIfTrue : valueIfFalse;
The whole thing is an expression — it produces a value, so you can assign it, pass it as an argument, or interpolate it into a template literal. That's what makes it different from if/else, which is a statement and doesn't have a value.
const age = 17;
const role = age >= 18 ? "adult" : "minor";
console.log(`You are a ${age >= 18 ? "n adult" : " minor"}.`);
// "You are a minor."
function fee(member) {
return member ? 0 : 20;
}Ternary vs if/else
Both can express the same logic, but they fit different shapes.
When ternary wins
// Short, both branches produce a value, no side effects
const label = isAdmin ? "Admin" : "User";
const className = isActive ? "row active" : "row";
const greeting = `Hi, ${name || "friend"}`;When if/else wins
// Side effects, multiple statements, early returns
if (user.banned) {
log("blocked", user.id);
notifyOps(user);
return;
}
// Multi-line branches read awfully as ternaries — don't.Nesting (sparingly)
Ternaries can nest because each branch is itself an expression. They become hard to read fast.
Readable nested ternary
const status = user.banned ? "banned" : user.verified ? "active" : /* otherwise */ "pending"; console.log(status);
The trick that makes this readable is the aligned layout: each condition on its own line, results aligned in a column, with the final fallback labeled. Read top to bottom, it works like a tiny lookup table.
Unreadable nested ternary
const status = user.banned ? "banned" : user.verified ? "active" : user.invited ? "pending" : "none";
Common patterns
Pluralisation
function items(n) {
return `${n} item${n === 1 ? "" : "s"}`;
}
console.log(items(0)); // "0 items"
console.log(items(1)); // "1 item"
console.log(items(7)); // "7 items"Default with a guard
const heading = title ? title : "Untitled"; // Often simpler with || or ??: const heading2 = title || "Untitled"; const heading3 = title ?? "Untitled";
Class name selection
const className = [
"btn",
size === "lg" ? "btn-lg" : "btn-md",
disabled ? "btn-disabled" : "",
].filter(Boolean).join(" ");In JSX
// {user
// ? <p>Welcome, {user.name}</p>
// : <p>Please sign in.</p>}Pitfalls
Confusing precedence
// Looks like "(a + 1) ? b : c" const x = a + 1 ? b : c; // works — + binds tighter than ?: // But mixing assignment in branches can surprise you const y = ok ? (a = 1) : (b = 1); // assigns AND returns the value
Side effects in branches
// Both branches evaluate? No — only the chosen one. const result = condition ? expensive() // runs ONLY if condition is truthy : cheap(); // runs ONLY if condition is falsy
Ternaries short-circuit — only the chosen branch is evaluated. That's a feature, but it can hide work in a branch where you didn't expect it.
Ternary vs short-circuit (&&, ||, ??)
There's an overlap with the logical operators. A quick guide:
a ? a : b→ usea || b(ora ?? bif0/""/falseare valid fora).cond ? doThing() : null→ usecond && doThing().cond ? x : ywhere bothxandymatter → keep the ternary.
// Same effect — three ways const userName = name || "Guest"; const userName2 = name ?? "Guest"; const userName3 = name ? name : "Guest"; // Prefer the shortest one that's faithful to the intent
Style and formatting
Prettier and most other formatters wrap a ternary across three lines when it gets long:
const message =
err instanceof NetworkError
? "Check your connection and try again."
: "Something went wrong, please reload.";The question mark and colon align with the wrapped indentation, which makes the two branches visually distinct.