Skip to content
JavaScript js control-flow 4 min read

if / else Statements

Conditionals are how a program makes decisions. The if statement runs a block of code only when a condition is met, and else if / else let you handle the alternatives. Mastering conditionals — and knowing when to flatten them — is one of the biggest factors in how readable your JavaScript turns out to be.

The basic if statement

An if statement evaluates an expression and executes its block when the result is truthy. The condition lives in parentheses; the body lives in braces.

const temperature = 32;

if (temperature > 30) {
  console.log("It's hot outside — stay hydrated.");
}

Output:

It's hot outside — stay hydrated.

You can technically omit the braces for a single statement, but don’t. Always use braces — it prevents a whole class of bugs where a second line silently falls outside the conditional.

Tip: A common JavaScript footgun is writing if (x = 5) (assignment) instead of if (x === 5) (comparison). The single = assigns and then evaluates truthily, so the block always runs. Linters flag this — keep one enabled.

Adding else and else if

When you need a fallback, use else. When you need multiple mutually exclusive branches, chain else if. JavaScript evaluates each condition top to bottom and runs the first matching block, skipping the rest.

function gradeFor(score) {
  if (score >= 90) {
    return "A";
  } else if (score >= 80) {
    return "B";
  } else if (score >= 70) {
    return "C";
  } else {
    return "F";
  }
}

console.log(gradeFor(95));
console.log(gradeFor(83));
console.log(gradeFor(40));

Output:

A
B
F

Because order matters, put the most specific or most likely conditions first. Once a branch matches, the rest are never checked.

Truthy and falsy conditions

The condition doesn’t have to be a boolean — JavaScript coerces it. Any value is either truthy or falsy. There are only a handful of falsy values; everything else is truthy.

Falsy valuesNotes
falsethe boolean itself
0, -0, 0nnumeric and BigInt zero
""empty string
nullintentional absence
undefineduninitialized / missing
NaN”not a number”
const name = "";

if (name) {
  console.log(`Hello, ${name}`);
} else {
  console.log("Name is empty.");
}

Output:

Name is empty.

Gotcha: Empty arrays [] and empty objects {} are truthy, not falsy. To check for an empty array, test arr.length === 0 instead of relying on the value itself.

Combining conditions with logical operators

Use && (and), || (or), and ! (not) to build compound conditions. Both && and || short-circuit: evaluation stops as soon as the result is known, which is useful for guarding against errors.

const user = { name: "Ada", isAdmin: true };

if (user && user.isAdmin) {
  console.log(`${user.name} has admin access.`);
}

The nullish coalescing operator ?? pairs nicely with conditionals when you only want to fall back on null or undefined (not on 0 or ""):

const config = { retries: 0 };
const retries = config.retries ?? 3; // 0, because 0 is not nullish
console.log(`Retries: ${retries}`);

Output:

Retries: 3

Wait — config.retries is 0, so ?? keeps it. The log above prints Retries: 0. Use ?? precisely when a valid 0 or "" must survive.

Nesting vs early returns

Deeply nested if statements (the “arrow anti-pattern”) are hard to read. Compare a nested version with a flattened one using guard clauses — early returns that handle invalid cases up front and let the happy path stay un-indented.

// Nested — harder to follow
function checkout(cart) {
  if (cart) {
    if (cart.items.length > 0) {
      if (cart.paymentMethod) {
        return "Order placed!";
      }
    }
  }
  return "Cannot check out.";
}

Rewritten with guard clauses:

function checkout(cart) {
  if (!cart) return "Cannot check out: no cart.";
  if (cart.items.length === 0) return "Cannot check out: cart is empty.";
  if (!cart.paymentMethod) return "Cannot check out: no payment method.";

  return "Order placed!";
}

console.log(checkout({ items: [], paymentMethod: "card" }));
console.log(checkout({ items: ["book"], paymentMethod: "card" }));

Output:

Cannot check out: cart is empty.
Order placed!

Each guard clause states one requirement and exits immediately, so the main logic reads like a straight line.

Ternary for simple value selection

When an if/else only picks between two values, the conditional (ternary) operator is more concise. Reserve it for expressions — not for branching side effects.

const age = 20;
const ticket = age >= 18 ? "Adult" : "Child";
console.log(ticket);

Output:

Adult

Avoid nesting ternaries more than one level deep; reach for if/else if or a switch once you have three or more branches.

Best practices

  • Always use braces, even for one-line bodies — it prevents accidental scope bugs.
  • Use strict equality (=== / !==) so you never get surprised by type coercion.
  • Prefer guard clauses and early returns over deep nesting to keep the happy path flat.
  • Order else if chains from most specific to least specific.
  • Use ?? (not ||) when 0, "", or false are legitimate values to keep.
  • Reach for a ternary only when selecting a value; use a full if when running statements.
  • Once you have many discrete cases on a single variable, consider switch for clarity.
Last updated June 1, 2026
Was this helpful?