The Ternary Operator
The ternary operator (? :) is JavaScript’s only operator that takes three operands. It evaluates a condition and returns one of two values, making it the most concise way to express a choice. Because it is an expression rather than a statement, you can use it anywhere a value is expected — inside assignments, function arguments, template literals, and JSX. Used well it makes code shorter and clearer; nested carelessly it becomes a puzzle.
Syntax
The form is always condition ? valueIfTruthy : valueIfFalsy. JavaScript evaluates the condition, coercing it to a boolean, then returns the expression after ? when truthy or the expression after : when falsy.
const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status);
Output:
adult
Only the chosen branch is evaluated — the other is skipped entirely. This short-circuiting means side effects in the untaken branch never run, which matters when a branch calls a function or performs a computation.
const fallback = () => {
console.log("fallback ran");
return "default";
};
const value = true ? "primary" : fallback();
console.log(value);
Output:
primary
Notice fallback ran is absent — the falsy branch was never touched.
Ternary vs if statement
An if statement controls flow; the ternary produces a value. They are not interchangeable. Reach for the ternary when you are picking between two values; reach for if when you are deciding between two actions or running multiple statements.
| Concern | Ternary ? : | if statement |
|---|---|---|
| Returns a value | Yes (it’s an expression) | No (it’s a statement) |
| Use inline (in assignment, JSX, template) | Yes | No |
| Run multiple statements per branch | No | Yes |
| Best for | Choosing between two values | Branching program flow |
// Good: choosing a value
const greeting = isLoggedIn ? `Welcome back, ${name}` : "Please sign in";
// Better as an if: doing things
if (isLoggedIn) {
trackLogin(name);
renderDashboard();
} else {
redirectToLogin();
}
Tip: If you ever write
x ? doThing() : undefinedpurely for the side effect, you want anifstatement instead. The ternary is for values, not for triggering one-sided actions.
Common uses
Inline assignment and arguments
The ternary shines when a value depends on a condition. It keeps the decision next to where the value is used.
const discount = isMember ? 0.1 : 0;
const price = base * (1 - discount);
console.log(formatCurrency(total > 100 ? total * 0.95 : total));
Inside template literals and JSX
Because if cannot appear inside an expression, the ternary is the standard tool for conditional text in template literals and conditional rendering in JSX-style code.
const items = 1;
const label = `${items} item${items === 1 ? "" : "s"} in cart`;
console.log(label);
Output:
1 item in cart
// React/JSX: pick which element to render
function Status({ online }) {
return <span>{online ? "🟢 Online" : "⚪ Offline"}</span>;
}
Default-style values
A ternary can express a fallback, though for nullish defaults the ?? operator is usually cleaner.
const name = input ? input.trim() : "Anonymous";
// For null/undefined-only fallbacks, prefer:
const port = config.port ?? 3000;
Chaining and readability
Ternaries can be chained because the falsy branch can itself be another ternary. This mimics an if / else if / else ladder, but readability degrades fast.
const grade =
score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: "F";
console.log(grade);
When formatted as a vertical ladder like above, a two- or three-level chain stays readable. Beyond that, the logic is hard to scan, and a real branching structure — an if/else if chain, a switch, or a lookup object — communicates intent better.
// Clearer than a deep ternary chain for discrete mappings
const labels = { draft: "Draft", review: "In review", live: "Published" };
const display = labels[state] ?? "Unknown";
Warning: Avoid nesting a ternary inside the truthy branch (
a ? b ? c : d : e). It reads ambiguously and is a frequent source of bugs. If you must chain, keep the nesting in the falsy position so it reads top-to-bottom.
Operator precedence is also a common pitfall. The ternary has very low precedence, so wrap it in parentheses when embedding it in larger expressions.
// Surprising: + binds tighter than ?:, but assignment is fine here.
const message = "Total: " + (paid ? "received" : "pending");
console.log(message);
Output:
Total: pending
Best Practices
- Use the ternary to choose between two values; use
ifto choose between two actions or multiple statements. - Keep each branch a simple expression — extract complex logic into a named variable or function first.
- Limit chains to two or three levels and format them as a vertical ladder for scanability.
- Never nest a ternary in the truthy branch; if you chain, nest only in the falsy branch.
- Prefer
??for null/undefined defaults and lookup objects for discrete value maps over deep ternary chains. - Wrap ternaries in parentheses when combining them with other operators to avoid precedence surprises.