Conditional Rendering
User interfaces are rarely static—you show a spinner while loading, an error banner when something fails, and the real content once data arrives. React has no special template syntax for this; conditional rendering is just plain JavaScript producing different values that React then renders. Because JSX is an expression, you decide what to render using the same if, ternary, and logical operators you already know, and React renders nothing at all for null, false, and undefined.
Branching with an early return
The simplest way to render different trees is to branch with a regular if statement and return early. This works because the body of a component is ordinary JavaScript—the rules of JSX only apply once you are inside a return.
function Profile({ user }) {
if (!user) {
return <p>Please log in to view your profile.</p>;
}
return (
<section>
<h1>{user.name}</h1>
<p>{user.email}</p>
</section>
);
}
Early returns keep each branch flat and readable, and they’re ideal for guard clauses such as loading and error states. The downside is that you can’t use if inside JSX—statements aren’t allowed between the tags—so for inline choices you reach for the ternary or &&.
The ternary operator
When you need to choose between two pieces of UI right inside the markup, the conditional (ternary) operator condition ? a : b is the tool, because it is an expression and therefore legal inside { }.
function Status({ isOnline }) {
return (
<p>
You are currently{" "}
<strong>{isOnline ? "online" : "offline"}</strong>.
</p>
);
}
Ternaries also nest whole elements, not just text:
function Dashboard({ loading, data }) {
return (
<div>
{loading ? <Spinner /> : <Report data={data} />}
</div>
);
}
Avoid deeply nested ternaries (
a ? b : c ? d : e). They become hard to scan quickly. When you reach a third branch, switch to an early return or extract a variable.
Short-circuit rendering with &&
To render something or nothing—a one-sided condition—the logical AND operator is the idiomatic choice. In JavaScript, condition && element evaluates to element when the condition is truthy and to the condition’s falsy value otherwise. Since React renders nothing for false, null, and undefined, the element simply disappears when the condition is false.
function Inbox({ unread }) {
return (
<header>
<h1>Inbox</h1>
{unread > 0 && <span className="badge">{unread} new</span>}
</header>
);
}
When unread is 0, nothing renders after the heading. This pattern is clean for badges, alerts, and optional sections.
The falsy 0 pitfall
There is one infamous trap. If the left side of && is the number 0 (or an empty string), && returns that value, and React renders 0 because numbers are valid children. The expression {items.length && <List />} prints a literal 0 on screen when the list is empty.
// Bug: renders "0" when items is empty
{items.length && <List items={items} />}
// Fix: force a real boolean
{items.length > 0 && <List items={items} />}
| Left-hand value | value && <El /> evaluates to | React renders |
|---|---|---|
5 | <El /> | the element |
true | <El /> | the element |
0 | 0 | 0 — bug! |
"" | "" | nothing |
false | false | nothing |
null | null | nothing |
Always reduce the condition to a boolean with a comparison (> 0), Boolean(x), or !!x before using &&.
Returning null to render nothing
A component can opt out of rendering entirely by returning null. This is the cleanest way to express “under this condition, produce no DOM at all,” and it’s especially useful for components that are sometimes irrelevant.
function Warning({ message }) {
if (!message) return null;
return <div className="warning" role="alert">{message}</div>;
}
Returning null does not prevent the component from running—its hooks still execute—it only means React mounts no output for it.
Extracting to variables for readability
When branches grow large, embedding them inline makes the return noisy. Compute the element into a variable above the return and drop it in with { }. This keeps the markup a flat, readable tree while letting you use full if/else logic.
function OrderStatus({ status }) {
let banner;
if (status === "shipped") {
banner = <p className="ok">Your order is on its way.</p>;
} else if (status === "delayed") {
banner = <p className="warn">Shipping is delayed.</p>;
} else {
banner = <p>Order received.</p>;
}
return (
<article>
<h2>Order #1042</h2>
{banner}
</article>
);
}
Output (when status is "delayed"):
Order #1042
Shipping is delayed.
This “variable holding JSX” pattern scales well to many states and pairs naturally with a switch statement or a lookup object mapping each status to its element.
Best Practices
- Use early returns for guard clauses like loading, error, and empty states—they keep branches flat.
- Reach for the ternary when choosing between exactly two elements inline.
- Use
&&for one-sided “show or nothing” conditions, but coerce the condition to a real boolean first. - Beware the
0pitfall: writecount > 0 && ..., nevercount && .... - Return
nullwhen a component should render no output at all. - Extract complex branches into a named variable above the
returninstead of nesting ternaries. - Keep render logic cheap—it runs on every render—and move heavy computation into memoized helpers.