The core difference

A JavaScript program is built from two fundamentally different kinds of ingredients: statements and expressions. An expression is any chunk of code that produces a value. A statement is an instruction that tells the computer to do something.

Common examples of expressions include literals like 1 or "hello", arithmetic like 5 * 10, comparisons like num > 100, ternary expressions like isHappy ? "🙂" : "🙁", and function calls like [1, 2, 3].pop(). Each one resolves to a single value.

Statements, on the other hand, are the structural commands of a program: variable declarations, if conditions, for loops, and function declarations all qualify. They don't produce a value—they perform an action.

let hi = 5;
if (hi > 10) {
  // More statements here
}
throw new Error('Something exploded!');

How they fit together

Think of statements as the skeleton of a program and expressions as the flesh. Statements contain "slots" where you can drop in any expression. A variable declaration is a perfect example:

let hi = /* some expression */;

The slot after = accepts any valid expression:

let hi = 1;
let hi = "hello";
let hi = 5 * 10;
let hi = num > 100;
let hi = isHappy ? "🙂" : "🙁";
let hi = [1, 2, 3].pop();

This interchangeability is what makes JavaScript flexible. If a statement has an expression slot, you can plug in anything that resolves to a value and the code will be syntactically valid—though it might still cause logical problems at runtime, such as an infinite loop:

while ("hello") {
  // Because “hello” never changes, this loop will
  // run over and over until the script crashes.
  // Syntactically valid, but still problematic.
}

A quick test for expressions

If you're ever unsure whether a piece of code is an expression or a statement, try passing it to console.log():

console.log(/* Some chunk of JS here */);

If the code runs and prints something, it's an expression. If it throws an error, it's a statement. This works because function arguments must always be expressions—they produce a value that gets handed to the function. Statements don't produce values, so they can't be passed as arguments.

The thin line between them

Expressions can't exist entirely on their own in a JavaScript file. They are always wrapped inside a statement, even if that statement contributes nothing extra. Consider a file containing only 1 + 2 + 3:

1 + 2 + 3

That file technically contains one statement: an expression statement that is empty apart from its expression slot.

/* expression slot */;

So each of these lines is a perfectly valid statement:

// Statement 1:
let hi = /* expression slot */;

// Statement 2:
return /* expression slot */;

// Statement 3:
if (/* expression slot */) { }

// Statement 4:
/* expression slot */;

Tutorials often blur the lines here, claiming expressions are statements. The truth is subtler: statements can wrap an expression without adding any visible characters. A semi-colon typically marks the end of a statement, but constructs like if blocks, while loops, and function declarations don't require one.

Why this matters in React

JSX lets you embed JavaScript inside curly braces:

function CountdownClock({ secondsRemaining }) {
  return (
    <div>
      Time left:
      {Math.round(secondsRemaining / 60)} minutes!
    </div>
  );
}

But there's a hard rule: only expressions can go inside those braces. They create an expression slot within the JSX. Trying to embed a statement—like an if/else block—produces an error:

function CountdownClock({ secondsRemaining }) {
  return (
    // 🚫 Throws a SyntaxError
    <div>
      {if (secondsRemaining > 0) {
        `${secondsRemaining} seconds left`
      } else {
        "Time expired!"
      }}
    </div>
  );
}

To include conditional logic in JSX, you must convert the statement into an expression, typically using a ternary operator:

function CountdownClock({ secondsRemaining }) {
  return (
    // ✅ No problemo
    <div>
      {secondsRemaining > 0
        ? `${secondsRemaining} seconds left`
        : "Time expired!"
      }
    </div>
  );
}

This might look like a React quirk, but it's actually a JavaScript constraint. React is simply surfacing a language limitation. The same reasoning explains many other React errors that seem arbitrary at first glance. Understanding the statement/expression distinction is the first step to demystifying them—though JSX compilation and the render cycle are separate topics worth exploring on their own.

Key takeaway

A program is a sequence of statements, each one an instruction to do something. Expressions produce values, and those values slot into statements the way cartridges slot into a console. Expressions are always contained within a statement, even a bare one. For example, a loop can run without a for statement, but it still lives inside an empty wrapper statement:

data.forEach(item => console.log(item));

This distinction takes time to internalize, but once it clicks, a wide range of JavaScript warnings and runtime surprises start to make sense.