JavaScript Conditional Statements with examples

Conditional statements are the backbone of decision-making in JavaScript.
They let your code think logically if this, then that.
Let’s dive deep into how JavaScript handles conditions, from simple if checks to the modern ?? (nullish coalescing) and optional chaining operators.
1. The Core Fundamentals: if, else, and else if.
What are Conditional Statements?
conditional statements let you execute code only if certain conditions are true.
In JavaScript, these questions evaluate to a truthy or falsy value (not strictly true/false only).
For example: 0, ‘ ‘, null, undefined and NAN are all falsy; everything else (including objects, arrays, non-zero numbers) is truthy.
That means when you write if (someValue) {…}, JavaScript first converts someValue to a boolean under the hood.

1.1 How does the if statement work?
The if statement is the most basic form of conditional logic. The code block within the curly braces ({}) runs only if the specified condition evaluates to true.
Syntax:
if (condition) {
// code runs only if condition is truthy
}Example:
let score = 85;
if (score > 80) {
console.log("Great job!");
}
// Output: Great job!Use case: You want to run a block of code only when a specific condition is met, and otherwise do nothing.
Best-practices: Always use curly braces { … } even if you have only one statement inside the if. This avoids errors especially when code is later modified.
When to use if…else and else if?
1.2 The if…else statement
When you need two distinct paths (condition met vs not met) you use if…else.
Syntax:
if (condition) {
// code if condition is true
} else {
// code if condition is false
}Example (real-world):
const balance = 500;
const phonePrice = 600;
if (phonePrice <= balance) {
console.log("You can purchase the item!");
} else {
console.log("Insufficient funds.");
}
// Output: Insufficient funds.Use case: when you must handle both “yes” and “no” branches (e.g., form valid vs invalid, user logged in vs not).
1.3 The if else, else if ladder
When there are more than two possible outcomes, you chain else if blocks. JavaScript checks each condition in order; the first one that’s true runs, and the rest are skipped. Example:
let grade = 87;
if (grade >= 90) {
console.log("A");
} else if (grade >= 80) {
console.log("B"); // runs here since 87 ≥ 80
} else if (grade >= 70) {
console.log("C");
} else {
console.log("F");
}
// Output: BUse case: grading systems, tiered discounts, multi-status workflows.
Key points to mention:
The order of conditions matters.
Once one else if matches, the rest are ignored.
The final else acts as the “catch-all” fallback.
Don’t forget that each else if needs its own condition.
1.4 Nested conditions (complex logic)
Sometimes you need to check a condition inside another condition, this is called nesting:
let weather = "sunny";
let temp = 25;
if (weather === "sunny") {
if (temp > 30) {
console.log("It's a hot day!");
} else if (temp > 20) {
console.log("It's a warm day."); // Output: It's a warm day.
} else {
console.log("Cool day.");
}
}
// The inner conditions only run if the outer condition (`weather === "sunny"`) is true.Use case: multi-step decision processes (e.g., user logged in → check role → check permissions).
Best-practices: Deep nesting quickly becomes hard to read and maintain. Aim to avoid “if-hell”. Use guard clauses (return early), or flatten logic when possible.
2. Efficiency and Syntax Shortcuts
Now that you have the basics, let's look at some shortcuts and better patterns.
2.1 What is the Ternary Operator and when should you use it?
This is JavaScript’s only operator with three operands, which is a concise alternative to simple else … if.
Syntax:
condition ? expressionIfTrue : expressionIfFalseExample:
let age = 21;
const result = (age >= 18) ? "Eligible to vote" : "Not eligible to vote";
console.log(result); // Output: Eligible to voteChaining ternaries:
let score = 85;
let grade = (score >= 90) ? "A" : (score >= 75) ? "B" : "C";
console.log(grade); // Output: BUse case: Setting a variable based on a condition, in one line.
Caveat: Avoid deeply nested ternary expressions, it hurts readability. Use full if … esle when logic gets complex.
2.2 Logical Operators ( &&, | |, !)
Logical operators allow you to combine conditions:
&& (AND): true only if both operands are true
| | (OR): true if either operand is true
! (NOT): negates the boolean value of its operand
Example using && and | |:
let price = 100;
let inStock = true;
if (price < 150 && inStock) {
console.log("Purchase allowed.");
}
let isWeekend = false;
let isHoliday = true;
if (isWeekend || isHoliday) {
console.log("Special opening hours.");
}Common mistake (in JavaScript):
let x = 10;
if (x === 5 || 7 || 10) {
// This is broken — “7” and “10” are truthy constants, so condition always true
}Correct is:
if (x === 5 || x === 7 || x === 10) {
// correct
}Use case: combining multiple criteria, toggling flags, checking presence of conditions.
Tip: Be explicit with each comparison, don’t rely on “or’ing” literals loosely.
2.3 Short-Circuit Evaluation
Logical operators in JS use “short-circuit”:
For &&, if the left operand is falsy, the right operand is not evaluated (because the whole expression must be falsy).
For | |, if the left operand is truthy, the right operand is not evaluated.
Use case (error prevention):
let n = 0;
// Want to check modulo only if n ≠ 0
if (n !== 0 && x % n === 0) {
// safe
}
Here, if n !== is false, x % n === 0 is never evaluated (avoiding division by zero). Short-circuiting is a powerful technique.

3.How does the switch statement simplify multiple conditions?
Beyond basic if…else, JavaScript has additional tools that can make code more expressive, maintainable, and modern.
3.1 The switch Statement
When you’re comparing one variable against many fixed values, switch can be clearer than a long if…else , else if chain.
Syntax:
let day = 3;
let dayName;
switch (day) {
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday"; // match
break;
default:
dayName = "Invalid day";
break;
}
console.log(dayName); // Output: WednesdayKey points:
The break statement is crucial. Without it, execution will “fall through” to subsequent cases.
switch uses strict equality ( === ) when comparing case labels.
Use case blocks for discrete, fixed values, not complex boolean logic (like <50 , &&, etc), for that you may still need if statement.
Alternative pattern:
For simple mappings (input → result), many developers prefer an object literal or Map over a switch, because it's cleaner and easier to maintain:
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple']
};
function getFruitsByColor(color) {
return fruitColor[color] || [];
}This approach reduces boilerplate and improves readability.
3.2 How does Optional Chaining ?.) make code safer?
Introduced in modern JavaScript, optional chaining helps safely access properties or call methods on objects that might be null or undefined, without throwing a runtime error.
Example:
const user = {
profile: { name: "Alice" }
};
// Traditional (verbose) way
const userName = user.profile && user.profile.name;
// With optional chaining:
const userName2 = user.profile?.name; // "Alice"
const userCity = user.location?.city; // undefined (instead of error)It works with methods too:
const result = someInterface.customMethod?.(); // calls if exists, else undefinedUse case: when dealing with deeply nested objects, user data that may or may not have certain fields, APIs with optional properties.
3.3 What is Nullish Coalescing ?? ) and how is it different from | |?
With | |, you might accidentally override valid “falsy” values like 0, “ “, false. The nullish coalescing operator solves this by returning the right-hand operand only when the left is null or undefined.
Example:
const userCount = 0;
const defaultCount = userCount ?? 10; // result: 0 (preserves 0)Compare:
userCount || 10 → 10 (because 0 is falsy)
userCount ?? 10 → 0 (because 0 is not null or undefined)
Use case: setting defaults where valid values might be 0, “ “, false, and you don't want them to be overwritten.
Combined with optional chaining:
const customer = {};
const customerCity = customer.details?.address?.city ?? "Unknown city";
console.log(customerCity); // "Unknown city"3.4 Modern Array Methods for Multiple Criteria
Instead of writing long chains of | | or nested ifs to check lists of values, you can lean on array methods like .includes(), .some(), .every() for cleaner code.
Example with .includes():
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
let fruit = 'apple';
if (redFruits.includes(fruit)) {
console.log('It is a red fruit.');
}Example with .some(), / .every()
const fruits = [{ color: 'red' }, { color: 'yellow' }];
// Check if any fruit is red
const isAnyRed = fruits.some(f => f.color === 'red');
console.log(isAnyRed); // true
// Check if all fruits are red
const allFruits = [{ color: 'red' }, { color: 'red' }];
const isAllRed = allFruits.every(f => f.color === 'red');
console.log(isAllRed); // trueBenefits: readability improves, conditions are easier to maintain, adding new values is a matter of updating the array instead of expanding if logic.
4. Best Practices for Writing Clean Conditional Logic
Now that we’ve covered the various techniques, let’s nail down best practices so your code stays readable, maintainable and bug-free.
Use strict equality ( === )
Always favour === over ==, and !== over !=, unless you have a very clear reason to do otherwise. Loose equality (==) allows type coercion which often causes bugs (for example: “0” == 0 → true). Use strict equality, it checks both value and type.
Return early (guard clauses)
Avoid deep nesting by returning early when invalid or special conditions are encountered. For example:
function processFruit(fruit) {
if (!fruit) {
throw new Error('No fruit provided!');
}
// proceed with main logic
if (redFruits.includes(fruit)) {
// …
}
}Instead of:
if (fruit) {
if (redFruits.includes(fruit)) {
// …
}
}The “early return” style keeps your primary logic less indented, easier to read.
Avoid over-nesting & long if…else chains
When there are many conditions or deep nested logic, code becomes hard to follow. Alternatives:
Use array methods ( .includes(), .some(), .every())
Replace condition chains with object literals / maps
Refactor nested logic into small functions
Use switch where appropriate rather than many else ifs
Many devs debate whether to use nested ternary operators. While technically valid, they often hurt readability and maintainability. One commenter wrote: Reddit
“It is so so so difficult to understand JS even in it's simplest forms for me since I am a beginner.”
Choose the right tool
For simple true/false checks: if is fine
For two branches: if…else or ternary (if simple)
For many values of one variable: switch or object literal mapping
For nested complex logic: consider separating into functions instead of deep ifs
For checking lists: use array methods ( .includes() .some(), .every()) instead of long | | chains
Comment wisely & keep it readable
Even the best logic gets tricky later. Use descriptive variable names (e.g., isWeekend, isAdmin), avoid magic numbers (use constants), and add small comments when necessary, but don't over-comment trivial logic. Readable code is better than commented unclear code.
5. Real-World Use Cases & Common Mistakes
Use-case: Form validation
function validateForm(user) {
if (!user) {
throw new Error('No user data');
}
if (!user.email) {
return { valid: false, error: 'Email missing' };
}
if (!user.password || user.password.length < 8) {
return { valid: false, error: 'Password too short' };
}
return { valid: true };
}Here you see guard clauses (early returns) and simple ifs for checking conditions. No deep nesting. Clear error paths.
Use-case: Feature toggle / role based logic
if (user.isLoggedIn && user.role === 'admin') {
showAdminPanel();
} else if (user.isLoggedIn) {
showUserDashboard();
} else {
showLoginScreen();
}This uses logical operators + else if ladder.
Common mistakes
Not using braces ( { … } ) leads to ambiguous behavior (dangling else).
Over-nesting conditionals without flattening logic.
Using | | incorrectly for multiple equality checks (e.g., x=== 5 || 7), logically wrong.
Relying on | | for default values when falsy values matter (should use ??).
Writing very long if … else , if … else chains when a mapping/object would be cleaner.
Conclusion:
Conditional logic is everywhere in your JavaScript code, from simple form validations to complex workflow branching. By mastering the basics and adopting modern features and best practices, you’ll write code that is both powerful and maintainable.
As you work on your projects (especially with your MERN / Next.js stack), when you see “many branches”, “nested logic”, or “many checks”, pause: maybe you can replace a chain with a mapping, or extract logic into a helper, or employ optional chaining or nullish coalescing. Clean logic today saves debug time tomorrow.
Join the conversation
Sign in to share your thoughts and engage with other readers.
No comments yet
Be the first to share your thoughts!