Himanshu SharmaSoftware engineer
Let’s talk

React · Lesson 10 of 38

Conditional rendering

Show different UI by data in React with if, the ternary operator, && and early returns, and avoid the bug where a 0 appears on the page.

14 minComponents and propsUpdated 24 October 2026

By the end of this lessonChoose between if, ternary, && and early returns to show, hide or swap parts of a component, and avoid rendering a stray 0.

Real interfaces change with the data: show a login button or a welcome message, a spinner or the results, an error or the content. React has no special v-if or template language for this. You use ordinary JavaScript: if, the ternary operator ? : and the logical &&. This lesson shows when to choose each.

if statements and early returns

A component is a function, so it can use if before it returns. This is the clearest choice when the whole result differs:

jsx
function Greeting({ user }) {
  if (!user) {
    return <p>Please sign in.</p>;
  }
  return <p>Welcome back, {user.name}!</p>;
}

function App() {
  return (
    <div>
      <Greeting user={null} />
      <Greeting user={{ name: "Asha" }} />
    </div>
  );
}

export default App;
result
Please sign in.
Welcome back, Asha!

The first return is an early return. It deals with the special case first and exits, so the rest of the function can assume user exists. Early returns keep components flat: compare this with an if/else wrapped around everything.

A component can return null to render nothing at all, which is the standard way to hide it:

jsx
function Notice({ message }) {
  if (!message) return null;
  return <p style={{ background: "#ffd45c", padding: 6 }}>{message}</p>;
}

function App() {
  return (
    <div>
      <Notice message="Backup finished." />
      <Notice message="" />
      <p>(The empty notice above renders nothing.)</p>
    </div>
  );
}

export default App;
result
Backup finished.
(The empty notice above renders nothing.)

The ternary operator

Inside JSX, you cannot write an if statement because JSX braces accept only expressions. The ternary operator condition ? a : b is an expression, so it fits:

jsx
function Status({ online }) {
  return (
    <p>
      <span style={{ color: online ? "green" : "gray" }}>●</span>{" "}
      {online ? "Online" : "Offline"}
    </p>
  );
}

function App() {
  return (
    <div>
      <Status online={true} />
      <Status online={false} />
    </div>
  );
}

export default App;
result
● Online
● Offline

The ternary can choose a value (color), a string, or whole chunks of JSX. For larger blocks, wrap them in brackets for readability:

jsx
function Profile({ user }) {
  return (
    <div>
      {user ? (
        <>
          <h3>{user.name}</h3>
          <p>{user.bio}</p>
        </>
      ) : (
        <p>No profile yet.</p>
      )}
    </div>
  );
}

function App() {
  return (
    <div>
      <Profile user={{ name: "Ravi", bio: "Loves databases." }} />
      <Profile user={null} />
    </div>
  );
}

export default App;
result
Ravi
Loves databases.
No profile yet.

Avoid nesting ternaries inside ternaries: the result is hard to read. If you need three or more cases, use an if with early returns, a switch, or an object lookup (shown below).

Logical AND (&&)

When something should either appear or not (there is no "else" branch), use &&. If the left side is true, React renders the right side; if it is false, the expression is false, which renders nothing:

jsx
function Message({ unread, isAdmin }) {
  return (
    <div>
      <p>Inbox</p>
      {unread > 0 && <p>You have {unread} unread messages.</p>}
      {isAdmin && <button>Admin panel</button>}
    </div>
  );
}

function App() {
  return (
    <div>
      <Message unread={3} isAdmin={true} />
      <hr />
      <Message unread={0} isAdmin={false} />
    </div>
  );
}

export default App;
result
Inbox
You have 3 unread messages.
Admin panel
Inbox

The zero trap

&& returns its left side when it is falsy. If that left side is the number 0, React happily renders it:

jsx
function Bad({ count }) {
  return <p>Bad: {count && <span>You have {count} items</span>}</p>;
}

function Good({ count }) {
  return <p>Good: {count > 0 && <span>You have {count} items</span>}</p>;
}

function App() {
  return (
    <div>
      <Bad count={0} />
      <Good count={0} />
      <Good count={2} />
    </div>
  );
}

export default App;
result
Bad: 0
Good:
Good: You have 2 items

"Bad: 0" is almost never what you want. Always make the left side a boolean: count > 0 &&, Boolean(count) && or !!count &&. The same trap applies to NaN and, for rendering "", nothing shows (an empty string renders as empty text).

Choosing between more than two cases

For a few states, an object works as a lookup table, and keeps the JSX tidy:

jsx
const MESSAGES = {
  loading: "Loading...",
  error: "Something went wrong.",
  empty: "No results found.",
};

function Results({ status, items }) {
  if (status !== "ready") {
    return <p>{MESSAGES[status] ?? "Unknown state"}</p>;
  }
  return <ul>{items.map(item => <li key={item}>{item}</li>)}</ul>;
}

function App() {
  return (
    <div>
      <Results status="loading" />
      <Results status="error" />
      <Results status="empty" />
      <Results status="ready" items={["Apple", "Banana"]} />
    </div>
  );
}

export default App;
result
Loading...
Something went wrong.
No results found.
Apple
Banana

This "status" pattern (loading, error, empty, ready) appears in nearly every screen that loads data, and you will use it again in the data-fetching lesson.

Conditionally choosing attributes and classes

The condition does not have to decide whether an element exists. It can change its props too:

jsx
function Task({ title, done }) {
  return (
    <li
      className={done ? "task done" : "task"}
      style={{ textDecoration: done ? "line-through" : "none", opacity: done ? 0.6 : 1 }}
    >
      {done ? "✔ " : "○ "}{title}
    </li>
  );
}

function App() {
  return (
    <ul style={{ listStyle: "none", padding: 0 }}>
      <Task title="Write lesson" done={true} />
      <Task title="Review lesson" done={false} />
    </ul>
  );
}

export default App;
result
✔ Write lesson
○ Review lesson

For classes made of several optional parts, build the string from an array: ["btn", primary && "btn-primary"].filter(Boolean).join(" ") gives "btn btn-primary" or just "btn".

Hiding with CSS versus not rendering

Not rendering a component (&&, null) removes it from the page, and from React's memory: its state is discarded, and it is created afresh next time. Hiding with style={{ display: "none" }} or a class keeps the element and its state. Choose not rendering for most cases; use CSS hiding when you want to preserve state, such as the text typed into a collapsed form, or for animations.

Common mistakes

  • Putting an if statement inside JSX braces. Move it above the return, or use a ternary.
  • Writing {count && <Item />} with a number on the left, which prints 0.
  • Nesting several ternaries until nobody can read them.
  • Forgetting that && has no "else": use a ternary when both branches matter.
  • Returning undefined by forgetting return in a branch. React 19 accepts undefined and renders nothing, but it is usually a bug.
  • Using display: none and expecting the component's state to reset.

Exercise

  1. Write Temperature({ celsius }) that shows "Freezing" below 0, "Cold" below 15, "Warm" below 28, and "Hot" otherwise, using early returns.
  2. Add a loggedIn prop to Greeting that shows a "Sign out" button when true and a "Sign in" button when false.
  3. Fix the zero trap in this expression: {cart.length && <p>Cart ({cart.length})</p>}.
  4. Extend the status lookup with a "offline" message and a default for any unknown status.

Key points

  • A component can return different JSX depending on its props and state; ordinary JavaScript decides which.
  • Use if with early returns for large differences, the ternary operator for either-or inside JSX, and && when something should appear or not.
  • null, false and undefined render nothing, so a component can return null to show nothing.
  • Put a real boolean on the left of &&: with a number such as 0 on the left, React renders the 0.

Check yourself

1. What does a component that returns null render?
2. What does {items.length && <List />} show when items is empty?
3. Which is best for choosing between two pieces of JSX inside a return statement?
4. Why use an early return like if (!user) return null?