Chaining Ternary Operators in JavaScript Tutorial
Learn how to chain multiple ternary operators in JavaScript for multi-way value selection. Covers formatting rules, readability guidelines, when chaining is appropriate, and safer alternatives.
The basic ternary operator handles two-way decisions: true or false, one value or the other. But what about three-way or four-way decisions? What if you need to return "A" for scores above 90, "B" for scores above 80, "C" for scores above 70, and "F" for everything else? Chaining ternary operators lets you express multi-way value selection in a single expression, similar to an else if chain but in expression form.
Chained ternaries are controversial. Used well with proper formatting, they are concise and elegant. Used poorly, they become the least readable code in your entire codebase. This tutorial teaches you the correct syntax, formatting rules that keep chained ternaries readable, practical use cases where they genuinely help, and the clear signals that tell you to use if else or a lookup object instead.
Chaining Syntax
A chained ternary places another ternary expression in the "false" branch of the previous one:
// Single ternary (2 outcomes)
condition ? valueA : valueB
// Chained ternary (3 outcomes)
condition1 ? valueA
: condition2 ? valueB
: valueC
// Chained ternary (4 outcomes)
condition1 ? valueA
: condition2 ? valueB
: condition3 ? valueC
: valueDEach : introduces a new branch. The last value (without a condition) acts as the default, similar to the else block in an if else chain:
function getGrade(score) {
return score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: score >= 60 ? "D"
: "F";
}
console.log(getGrade(95)); // "A"
console.log(getGrade(82)); // "B"
console.log(getGrade(55)); // "F"How JavaScript Evaluates Chained Ternaries
The ternary operator is right-associative, meaning it groups from right to left. This is what makes chaining work naturally:
// This expression:
a ? "A" : b ? "B" : "C"
// Is evaluated as:
a ? "A" : (b ? "B" : "C")
// NOT as:
(a ? "A" : b) ? "B" : "C" // This would be wrongJavaScript evaluates from left to right at runtime:
- Check
a. If true, return"A"and stop. - Check
b. If true, return"B"and stop. - If neither matched, return
"C".
This mirrors how else if chains evaluate: top to bottom, first match wins.
Formatting Rules for Readability
The difference between a readable chained ternary and an unreadable one is formatting. Follow these rules:
Rule 1: One Condition Per Line
// BAD: all on one line
const tier = points >= 1000 ? "gold" : points >= 500 ? "silver" : points >= 100 ? "bronze" : "basic";
// GOOD: each condition on its own line
const tier = points >= 1000 ? "gold"
: points >= 500 ? "silver"
: points >= 100 ? "bronze"
: "basic";Rule 2: Align the Colons
// GOOD: colons align vertically, creating a visual pattern
const label = status === "active" ? "Online"
: status === "idle" ? "Away"
: status === "offline" ? "Offline"
: "Unknown";
// ALSO GOOD: indent-style alignment
const label = status === "active" ? "Online"
: status === "idle" ? "Away"
: status === "offline" ? "Offline"
: "Unknown";Rule 3: Maximum Three to Four Conditions
| Chain Length | Readability | Recommendation |
|---|---|---|
| 2 conditions | Good | Single-line or two-line ternary |
| 3 conditions | Acceptable | Multi-line with formatting |
| 4 conditions | At the limit | Consider if else or lookup |
| 5+ conditions | Poor | Use if else, switch, or lookup object |
Stop at Four Conditions
If your chained ternary needs five or more conditions, it is too complex for an inline expression. Switch to an if else chain, a switch statement, or an object lookup. Readable code is always worth a few extra lines.
Practical Chained Ternary Examples
HTTP Status Categories
function getStatusCategory(code) {
return code >= 500 ? "server-error"
: code >= 400 ? "client-error"
: code >= 300 ? "redirect"
: code >= 200 ? "success"
: "informational";
}
console.log(getStatusCategory(200)); // "success"
console.log(getStatusCategory(404)); // "client-error"
console.log(getStatusCategory(503)); // "server-error"Dynamic Badge Colors
function getBadgeColor(priority) {
return priority === "critical" ? "#dc2626"
: priority === "high" ? "#ea580c"
: priority === "medium" ? "#ca8a04"
: "#16a34a"; // low or default
}
const style = { backgroundColor: getBadgeColor(ticket.priority) };Conditional Text in Templates
function formatTimeAgo(minutes) {
return minutes < 1 ? "just now"
: minutes < 60 ? `${Math.floor(minutes)} min ago`
: minutes < 1440 ? `${Math.floor(minutes / 60)} hr ago`
: `${Math.floor(minutes / 1440)} days ago`;
}
console.log(formatTimeAgo(0.5)); // "just now"
console.log(formatTimeAgo(25)); // "25 min ago"
console.log(formatTimeAgo(180)); // "3 hr ago"
console.log(formatTimeAgo(4320)); // "3 days ago"Clamping Values to Ranges
function clampToRange(value, min, max) {
return value < min ? min
: value > max ? max
: value;
}
console.log(clampToRange(5, 1, 10)); // 5
console.log(clampToRange(-3, 1, 10)); // 1
console.log(clampToRange(15, 1, 10)); // 10Chained Ternary vs If Else vs Switch
The same grading logic expressed three ways:
// Chained ternary (1 expression, compact)
const grade = score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: "F";
// If else chain (multi-statement, explicit)
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else {
grade = "F";
}
// Early return (cleanest in a function)
function getGrade(score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
return "F";
}| Approach | Lines | const-compatible | Best for |
|---|---|---|---|
| Chained ternary | 4 | Yes | Inline assignments, pure value selection |
| If else chain | 10 | No (needs let) | Complex logic, side effects |
| Early return | 5 | N/A (returns directly) | Functions that map input to output |
When Not to Chain Ternaries
When Branches Have Side Effects
// BAD: side effects in ternary branches
const result = isValid
? (logSuccess(), sendEmail(), "success")
: (logFailure(), showAlert(), "failure");
// GOOD: use if else for side effects
if (isValid) {
logSuccess();
sendEmail();
result = "success";
} else {
logFailure();
showAlert();
result = "failure";
}When Conditions Are Complex
// BAD: complex conditions in a chain
const access = user.isAdmin && !user.isSuspended ? "full"
: user.role === "editor" && resource.type === "article" ? "edit"
: user.isPremium || user.hasTrial ? "read"
: "none";
// GOOD: name the conditions, use if else
const hasFullAccess = user.isAdmin && !user.isSuspended;
const canEdit = user.role === "editor" && resource.type === "article";
const canRead = user.isPremium || user.hasTrial;
if (hasFullAccess) return "full";
if (canEdit) return "edit";
if (canRead) return "read";
return "none";When There Are Five or More Branches
// BAD: too many levels
const day = n === 0 ? "Sunday"
: n === 1 ? "Monday"
: n === 2 ? "Tuesday"
: n === 3 ? "Wednesday"
: n === 4 ? "Thursday"
: n === 5 ? "Friday"
: n === 6 ? "Saturday"
: "Invalid";
// GOOD: use an array or object lookup
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const day = days[n] ?? "Invalid";Best Practices
Chained Ternary Guidelines
Follow these rules to keep chained ternaries readable and maintainable.
Format with one condition per line. Never write a chained ternary on a single line. Each ? value : pair should have its own line, with colons aligned.
Limit to three or four conditions. Beyond four conditions, readability drops sharply. Use if else, switch, or an object lookup for longer chains.
Use only for pure value selection. Chained ternaries should select a value, not execute logic. If any branch has side effects, logging, or multiple statements, use if else.
Keep conditions simple. Each condition in the chain should be a single comparison or a named boolean. Complex multi-part conditions obscure the chain's structure.
Consider an object lookup first. When mapping exact values (strings, numbers) to results, an object is almost always shorter and clearer than a chained ternary. Reserve chaining for range-based conditions where lookups do not work.
Common Mistakes and How to Avoid Them
Chaining Pitfalls
These mistakes turn chained ternaries into maintenance nightmares.
Writing everything on one line. A five-condition chain on a single line is unreadable. Always break chained ternaries across lines.
Putting the chain in the true branch instead of the false branch. The standard pattern chains in the false branch: condition1 ? value1 : condition2 ? value2 : default. Chaining in the true branch (condition1 ? (condition2 ? value1 : value2) : value3) creates nested parenthetical logic that is much harder to follow.
Missing the default value. Every chained ternary should end with a default value (no condition on the last branch). Without it, the last branch requires a condition, and there is no fallback for unmatched inputs.
Using chained ternaries where a lookup works. If every condition is x === "value", replace the entire chain with an object: const result = map[x] ?? "default". Objects are more extensible and easier to read.
Next Steps
Master loops for repetitive tasks
With conditionals covered, learn how for loops and while loops let you repeat operations across data collections.
Combine ternaries with array methods
Use ternary expressions inside .map(), .filter(), and .reduce() callbacks for concise data transformations.
Practice refactoring if else to ternary
Take existing functions with simple if else assignments and rewrite them as ternary expressions. Then decide which version reads better.
Learn object destructuring for cleaner code
Combine ternary results with destructuring patterns for concise variable assignments from complex data structures.
Rune AI
Key Insights
- Chain in the false branch: the standard pattern is
condition1 ? value1 : condition2 ? value2 : default - One condition per line: never write chained ternaries on a single line; formatting determines readability
- Four conditions maximum: beyond four branches, switch to if else, switch, or object lookup
- Pure value selection only: chained ternaries should return values, not execute side effects or complex logic
Frequently Asked Questions
How many ternary operators can I chain together?
Are chained ternary operators bad practice?
How does JavaScript evaluate chained ternaries?
Can I mix ternary operators with other operators?
Should I use chained ternaries or switch statements?
Conclusion
Chained ternary operators let you express multi-way value selection in a single expression, replacing short else if chains when the goal is assigning or returning one of several values. The key discipline is formatting: one condition per line, aligned colons, and a maximum of three to four conditions. Beyond that threshold, if else chains, switch statements, or object lookups are always more readable.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.