How to Use Conditional Branching in JavaScript: if, else, and the Ternary Operator
Programs rarely run in a straight line. They make decisions. Should the user see a welcome screen or a login form? Is the password valid or invalid? Does the shopping cart qualify for free shipping? Every decision in your code is a conditional branch, a point where the program chooses one path over another based on a condition.
JavaScript provides two main tools for conditional branching: the if...else statement for general decision-making and the ternary operator ? : for concise inline conditions. This guide covers both in depth, including how to chain multiple conditions, how to combine conditions with logical operators, and the critical mistakes that trip up developers at every level.
The if Statement
The if statement is the most fundamental control structure in JavaScript. It evaluates a condition and executes a block of code only if that condition is truthy.
Basic Syntax
if (condition) {
// This code runs only if condition is truthy
}
The condition inside the parentheses is evaluated and converted to a boolean. If the result is true (or any truthy value), the code block executes. If the result is false (or any falsy value), the block is skipped entirely.
let temperature = 35;
if (temperature > 30) {
console.log("It's hot outside!");
}
// Output: "It's hot outside!"
let temperature = 20;
if (temperature > 30) {
console.log("It's hot outside!");
}
// No output: condition is false, block is skipped
Any Expression Can Be a Condition
The condition does not have to be a comparison. Any value works because JavaScript converts it to a boolean:
let username = "Alice";
if (username) {
console.log(`Welcome, ${username}!`); // Runs: non-empty string is truthy
}
let emptyName = "";
if (emptyName) {
console.log("This won't run"); // Skipped: empty string is falsy
}
let items = [1, 2, 3];
if (items.length) {
console.log(`You have ${items.length} items`); // Runs: 3 is truthy
}
let emptyCart = [];
if (emptyCart.length) {
console.log("This won't run"); // Skipped: 0 is falsy
}
Multiple Statements in the Block
The curly braces { } group multiple statements together:
let score = 95;
if (score >= 90) {
console.log("Excellent!");
console.log(`Your score: ${score}`);
console.log("You earned a gold star ⭐");
}
// All three lines run because score is 95
Single-Statement if (Without Braces)
When the if block contains only one statement, the braces are technically optional:
if (score >= 90) console.log("Excellent!");
However, always use braces. Omitting them is a frequent source of bugs:
// DANGEROUS: no braces
if (score >= 90)
console.log("Excellent!");
console.log("Gold star!"); // This ALWAYS runs: it's not inside the if!
// The indentation is misleading. JavaScript sees it as:
if (score >= 90) {
console.log("Excellent!");
}
console.log("Gold star!"); // Runs regardless of score
// SAFE: always use braces
if (score >= 90) {
console.log("Excellent!");
console.log("Gold star!"); // Now both are conditional
}
Always wrap if blocks in curly braces, even for single statements. This eliminates an entire category of bugs caused by later adding a second statement without realizing the first statement was the only one inside the if.
The else Clause
The else clause provides an alternative code block that runs when the if condition is falsy.
Basic if...else
let age = 15;
if (age >= 18) {
console.log("You can vote");
} else {
console.log("You cannot vote yet"); // This runs
}
The structure creates a binary choice: one block runs or the other. Never both, never neither.
let hour = 14;
if (hour < 12) {
console.log("Good morning!");
} else {
console.log("Good afternoon!"); // This runs: 14 is not less than 12
}
Practical Examples
// Login check
let password = "secret123";
if (password === "secret123") {
console.log("Login successful");
} else {
console.log("Invalid password");
}
// Even/odd check
let number = 7;
if (number % 2 === 0) {
console.log(`${number} is even`);
} else {
console.log(`${number} is odd`); // This runs
}
// User greeting
let user = null;
if (user) {
console.log(`Welcome back, ${user.name}!`);
} else {
console.log("Please log in"); // This runs: null is falsy
}
The else if Chain
When you have more than two possible outcomes, use else if to chain multiple conditions. JavaScript evaluates each condition from top to bottom and executes the first block whose condition is truthy.
Basic else if
let score = 75;
if (score >= 90) {
console.log("Grade: A");
} else if (score >= 80) {
console.log("Grade: B");
} else if (score >= 70) {
console.log("Grade: C"); // This runs: first true condition
} else if (score >= 60) {
console.log("Grade: D"); // Skipped: a match was already found
} else {
console.log("Grade: F"); // Skipped
}
Order Matters
The conditions are evaluated in order, and only the first matching block runs. This means you must order conditions from most specific to least specific:
// WRONG: overly broad condition first
let temperature = 40;
if (temperature > 0) {
console.log("Above freezing"); // This ALWAYS runs for positive temps
} else if (temperature > 30) {
console.log("Hot"); // Never reached!
} else if (temperature > 20) {
console.log("Warm"); // Never reached!
}
// CORRECT: most specific condition first
if (temperature > 30) {
console.log("Hot"); // This runs for 40
} else if (temperature > 20) {
console.log("Warm");
} else if (temperature > 0) {
console.log("Cool");
} else {
console.log("Freezing");
}
The Final else is Optional
You do not need a final else clause. Without it, nothing happens if no condition matches:
let day = "Saturday";
if (day === "Monday") {
console.log("Start of work week");
} else if (day === "Friday") {
console.log("TGIF!");
} else if (day === "Saturday" || day === "Sunday") {
console.log("Weekend!"); // This runs
}
// No else: other days are simply ignored
However, adding a final else as a safety net is often good practice:
let status = getUserStatus();
if (status === "active") {
grantAccess();
} else if (status === "suspended") {
showSuspensionNotice();
} else if (status === "banned") {
redirectToBlockedPage();
} else {
// Safety net: handles unexpected values
console.error(`Unknown status: ${status}`);
redirectToErrorPage();
}
Real-World Example: HTTP Status Handling
let statusCode = 404;
if (statusCode >= 200 && statusCode < 300) {
console.log("Success!");
} else if (statusCode >= 300 && statusCode < 400) {
console.log("Redirection");
} else if (statusCode >= 400 && statusCode < 500) {
console.log("Client error"); // This runs for 404
} else if (statusCode >= 500) {
console.log("Server error");
} else {
console.log("Unknown status code");
}
The Conditional (Ternary) Operator ? :
The ternary operator is a shorthand for simple if...else statements. It is the only JavaScript operator that takes three operands, which is why it is called "ternary."
Syntax
let result = condition ? valueIfTrue : valueIfFalse;
The condition is evaluated. If truthy, the expression returns valueIfTrue. If falsy, it returns valueIfFalse.
Basic Examples
let age = 20;
let status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
let score = 45;
let passed = score >= 50 ? "Pass" : "Fail";
console.log(passed); // "Fail"
let hour = 14;
let greeting = hour < 12 ? "Good morning" : "Good afternoon";
console.log(greeting); // "Good afternoon"
Comparing if...else with the Ternary Operator
// Using if...else (5 lines)
let price = 100;
let message;
if (price > 50) {
message = "Expensive";
} else {
message = "Affordable";
}
// Using ternary (1 line)
let message = price > 50 ? "Expensive" : "Affordable";
Both produce the same result. The ternary operator is ideal when you need to assign a value based on a condition.
Ternary in Template Literals
The ternary operator works beautifully inside template literals:
let itemCount = 3;
console.log(`You have ${itemCount} item${itemCount === 1 ? "" : "s"} in your cart`);
// "You have 3 items in your cart"
let itemCount = 1;
console.log(`You have ${itemCount} item${itemCount === 1 ? "" : "s"} in your cart`);
// "You have 1 item in your cart"
Ternary in Function Arguments
function greet(name) {
console.log(`Hello, ${name ? name : "stranger"}!`);
}
greet("Alice"); // "Hello, Alice!"
greet(""); // "Hello, stranger!"
greet(undefined); // "Hello, stranger!"
When to Use the Ternary Operator
Use the ternary operator when:
- You need to assign one of two values based on a condition
- The expression is short and readable on a single line
- There are exactly two outcomes
Do not use the ternary operator when:
- You need to execute multiple statements
- The logic is complex
- Readability suffers
// GOOD: simple value assignment
let accessLevel = isAdmin ? "full" : "restricted";
// BAD: side effects, not value assignment
isLoggedIn ? showDashboard() : redirectToLogin();
// Use if...else for this instead:
if (isLoggedIn) {
showDashboard();
} else {
redirectToLogin();
}
// BAD: too complex for a ternary
let label = age < 13 ? "child" : age < 18 ? "teenager" : age < 65 ? "adult" : "senior";
// Use if...else if for this instead
Nested Ternaries: When to Use and When to Avoid
You can chain ternary operators to handle more than two outcomes, creating a nested ternary.
How Nested Ternaries Work
let score = 85;
let grade = score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: score >= 60 ? "D"
: "F";
console.log(grade); // "B"
This is equivalent to:
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else if (score >= 60) {
grade = "D";
} else {
grade = "F";
}
Formatting Nested Ternaries for Readability
If you do use nested ternaries, format them vertically with each condition on its own line:
// READABLE: each condition on its own line, aligned
let category = age < 2 ? "infant"
: age < 13 ? "child"
: age < 18 ? "teenager"
: age < 65 ? "adult"
: "senior";
// UNREADABLE: everything on one line
let category = age < 2 ? "infant" : age < 13 ? "child" : age < 18 ? "teenager" : age < 65 ? "adult" : "senior";
When to Avoid Nested Ternaries
Most style guides (including Airbnb's) discourage nested ternaries. ESLint has a no-nested-ternary rule that flags them.
// AVOID: nested ternary with complex conditions
let result = user && user.isActive
? user.role === "admin"
? user.hasMFA
? "Full admin access"
: "Admin, MFA required"
: "Regular user"
: "Not logged in";
// PREFER: if...else for complex logic
let result;
if (!user || !user.isActive) {
result = "Not logged in";
} else if (user.role === "admin") {
result = user.hasMFA ? "Full admin access" : "Admin, MFA required";
} else {
result = "Regular user";
}
A practical guideline: Use a single ternary for two outcomes. Consider a formatted nested ternary for three to four simple outcomes (like the grade example). For anything more complex, use if...else if or switch.
Multiple Conditions and Combining with Logical Operators
Real-world conditions are rarely simple. You often need to check multiple things at once. JavaScript's logical operators (&&, ||, !) let you combine conditions.
AND Operator: &&
Both conditions must be true:
let age = 25;
let hasLicense = true;
if (age >= 18 && hasLicense) {
console.log("You can drive"); // This runs: both conditions are true
}
// Practical example: form validation
let username = "alice";
let password = "secret123";
if (username.length >= 3 && password.length >= 8) {
console.log("Validation passed");
} else {
console.log("Username must be 3+ chars and password must be 8+ chars");
}
You can chain multiple && conditions:
let age = 25;
let hasID = true;
let isMember = true;
if (age >= 21 && hasID && isMember) {
console.log("Access granted to VIP area");
}
OR Operator: ||
At least one condition must be true:
let day = "Saturday";
if (day === "Saturday" || day === "Sunday") {
console.log("It's the weekend!"); // This runs
}
// Practical example: checking for invalid input
let input = "";
if (input === null || input === undefined || input === "") {
console.log("Please provide input");
}
NOT Operator: !
Inverts a boolean value:
let isLoggedIn = false;
if (!isLoggedIn) {
console.log("Please log in"); // This runs: !false is true
}
// Double negation for boolean conversion
let value = "hello";
if (!!value) {
console.log("Value exists"); // !!truthy is true
}
Combining Operators
When combining && and ||, remember that && has higher precedence than ||:
// && is evaluated before ||
let a = true, b = false, c = true;
console.log(a || b && c);
// Evaluated as: a || (b && c)
// = true || (false && true)
// = true || false
// = true
Use parentheses to make your intent clear:
// Without parentheses: relies on precedence knowledge
if (isAdmin || isEditor && hasPermission) { }
// Means: isAdmin || (isEditor && hasPermission)
// With parentheses: crystal clear
if (isAdmin || (isEditor && hasPermission)) { }
// Different meaning with different grouping
if ((isAdmin || isEditor) && hasPermission) { }
Real-World Combined Conditions
// Shipping eligibility
let orderTotal = 75;
let isPremiumMember = false;
let isLocalDelivery = true;
if (orderTotal >= 100 || isPremiumMember || isLocalDelivery) {
console.log("Free shipping!");
} else {
console.log(`Add $${100 - orderTotal} more for free shipping`);
}
// Age verification with edge cases
let age = 17;
let hasParentalConsent = true;
if (age >= 18 || (age >= 16 && hasParentalConsent)) {
console.log("Access granted"); // Runs: second condition is true
} else {
console.log("Access denied");
}
// Input validation with multiple checks
function validateEmail(email) {
if (!email || typeof email !== "string" || !email.includes("@")) {
return false;
}
return true;
}
console.log(validateEmail("alice@example.com")); // true
console.log(validateEmail("invalid")); // false
console.log(validateEmail("")); // false
console.log(validateEmail(null)); // false
Short-Circuit Evaluation in Conditions
The && and || operators use short-circuit evaluation, which means they stop evaluating as soon as the result is determined:
// && stops at the first falsy value
let user = null;
if (user && user.name) { // user is null (falsy), so user.name is NEVER evaluated
console.log(user.name);
}
// No error! Without short-circuiting, user.name would throw a TypeError
// || stops at the first truthy value
let name = "" || "Anonymous";
console.log(name); // "Anonymous" ("" is falsy, so || returns the next value)
This pattern is commonly used for safe property access:
// Safe access chain with &&
let user = { profile: { name: "Alice" } };
let name = user && user.profile && user.profile.name;
console.log(name); // "Alice"
// Modern alternative: optional chaining (?.)
let name = user?.profile?.name;
console.log(name); // "Alice" (cleaner syntax, same safety)
Guard Clauses: Early Returns with Inverted Conditions
Instead of deeply nested if blocks, use guard clauses to handle edge cases early:
// DEEPLY NESTED (hard to read)
function processOrder(order) {
if (order) {
if (order.items && order.items.length > 0) {
if (order.customer) {
if (order.customer.isVerified) {
// Finally, the actual logic
console.log("Processing order...");
} else {
console.log("Customer not verified");
}
} else {
console.log("No customer info");
}
} else {
console.log("No items in order");
}
} else {
console.log("No order provided");
}
}
// GUARD CLAUSES (clean and flat)
function processOrder(order) {
if (!order) {
console.log("No order provided");
return;
}
if (!order.items || order.items.length === 0) {
console.log("No items in order");
return;
}
if (!order.customer) {
console.log("No customer info");
return;
}
if (!order.customer.isVerified) {
console.log("Customer not verified");
return;
}
// The main logic, at the same indentation level
console.log("Processing order...");
}
Both versions do exactly the same thing, but the guard clause version is dramatically easier to read and maintain.
Common Mistake: Assignment = vs. Comparison === Inside if
This is one of the most common and dangerous bugs in JavaScript, and it can happen to anyone.
The Problem
let status = "active";
// MISTAKE: single = is assignment, not comparison!
if (status = "inactive") {
console.log("Account is inactive"); // This ALWAYS runs!
}
console.log(status); // "inactive" (the variable was changed!)
Here is what happens step by step:
status = "inactive"assigns the string"inactive"tostatus- The assignment expression returns the assigned value:
"inactive" "inactive"is a non-empty string, which is truthy- The
ifblock runs because the condition is truthy - The variable
statushas been permanently changed to"inactive"
This bug is especially nasty because:
- There is no error or warning (it is valid JavaScript)
- The
ifblock almost always runs (unless you assign a falsy value) - The variable is silently modified as a side effect
The Correct Code
let status = "active";
// CORRECT: triple === is comparison
if (status === "inactive") {
console.log("Account is inactive"); // Does NOT run
}
console.log(status); // "active" (unchanged)
When It Accidentally "Works"
The bug is especially hard to catch when the assigned value is falsy:
let count = 5;
// MISTAKE: assigns 0 to count
if (count = 0) {
console.log("This does not run"); // 0 is falsy, so the block is skipped
}
console.log(count); // 0 (but count has been changed!)
The if block does not run (because 0 is falsy), so the developer might think the code is correct. But count has been silently changed from 5 to 0.
How to Prevent This Mistake
1. Always use === for comparisons
This is the simplest prevention. If you never use = inside an if, you never make this mistake.
2. Enable ESLint's no-cond-assign rule
{
"rules": {
"no-cond-assign": "error"
}
}
ESLint will flag any assignment inside a condition as an error.
3. Yoda conditions (optional, less common)
Some developers write comparisons with the constant on the left:
// Yoda condition: constant on the left
if ("inactive" === status) {
console.log("Account is inactive");
}
// If you accidentally use =, you get an error:
if ("inactive" = status) {
// SyntaxError: Invalid left-hand side in assignment
// You can't assign to a string literal!
}
This style catches the bug at the syntax level, but many developers find it less readable. ESLint is a better solution.
Other Subtle Assignment Bugs
// Mistake in a loop condition
let items = getItems();
// WRONG: assigns null to items on every iteration
while (items = null) {
processItem(items);
}
// CORRECT
while (items !== null) {
processItem(items);
items = getNextItems();
}
// Mistake with return values
function checkUser(user) {
// WRONG: assigns "admin" to user.role
if (user.role = "admin") {
return true; // Always runs!
}
return false;
// CORRECT
if (user.role === "admin") {
return true;
}
return false;
// EVEN BETTER: just return the comparison
return user.role === "admin";
}
Summary
Conditional branching is how your programs make decisions:
ifevaluates a condition and runs its block only when the condition is truthy. Always use curly braces, even for single statements.elseprovides an alternative block that runs when theifcondition is falsy. Together,if...elsecreates a binary choice.else ifchains multiple conditions. They are evaluated top to bottom, and only the first matching block runs. Order from most specific to least specific.- The ternary operator (
condition ? valueA : valueB) is a concise alternative for simple two-outcome value assignments. Use it for short, readable expressions. - Nested ternaries can handle multiple outcomes but quickly become unreadable. Format them vertically or prefer
if...else iffor complex logic. - Logical operators (
&&,||,!) combine multiple conditions. Remember that&&has higher precedence than||. Use parentheses for clarity. - Guard clauses (early returns with inverted conditions) flatten deeply nested code and improve readability.
- Never use
=inside anifcondition. Always use===for comparisons. Enable ESLint'sno-cond-assignrule to catch this mistake automatically.
With conditional branching in your toolkit, you are ready to explore logical operators in much greater depth, including short-circuit evaluation, the nullish coalescing operator, and logical assignment operators.