Lesson 5 of 31
Conditional Rendering
The patterns for showing different UI based on data — the ternary, the &&; shortcut and its most infamous gotcha, early returns, and rendering nothing at all.
Almost every component needs to show different things depending on data: a
loading spinner versus real content, an error message versus none, a badge
that only appears sometimes. JSX has no dedicated "if tag" — you reach for
plain JavaScript, arranged around the fact that {} in JSX only accepts
expressions.
The ternary, for either/or
When there are exactly two outcomes, the conditional (ternary) operator reads naturally inline:
function Status({ isOnline }) {
return <p>{isOnline ? "Online" : "Offline"}</p>;
}This works because condition ? a : b is an expression — it evaluates to a
value, which is exactly what {} needs.
&&, for "render this or nothing"
When there's only one meaningful outcome and the other case is "show
nothing," && is the common shorthand:
function Inbox({ unreadCount }) {
return (
<div>
<h2>Inbox</h2>
{unreadCount > 0 && <span className="badge">{unreadCount} new</span>}
</div>
);
}&& evaluates its left side; if that's falsy, it returns the left side
without evaluating the right side at all (short-circuiting), and if it's
truthy, it evaluates and returns the right side. React renders false
(and null/undefined) as nothing, so unreadCount > 0 && <span>... reads
as "if there are unread messages, show the badge; otherwise render nothing."
The stray-zero trap
Here's the exact same pattern with a subtle but common mistake:
// Looks reasonable, has a bug
{unreadCount && <span className="badge">{unreadCount} new</span>}When unreadCount is 0, && returns 0 — because 0 is falsy, so
short-circuiting stops there and returns it as-is. But React renders numbers
as text. The badge disappears (correctly), but a stray, unstyled 0
appears on the page in its place. The fix is to make sure the left side of
&& is always an actual boolean, not a number that happens to be falsy:
{unreadCount > 0 && <span className="badge">{unreadCount} new</span>}This is worth internalizing as a habit — put a comparison, not a raw number
or string, on the left of && whenever the value could be 0 or "".
Early returns, for more complex branching
When there are several distinct states to handle — loading, error, empty, loaded — a chain of ternaries gets unreadable fast. Since a component is just a function, an ordinary early return works, and reads better than nesting:
function UserProfile({ status, user, error }) {
if (status === "loading") {
return <Spinner />;
}
if (status === "error") {
return <ErrorMessage message={error} />;
}
if (!user) {
return <p>No user found.</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}Each if is a statement living in the function body, outside any JSX —
which is exactly why it's allowed, unlike trying to put if directly inside
{}.
Rendering nothing
Sometimes the correct output really is nothing. Returning null (or false
or undefined) is the normal way to say that:
function Banner({ dismissed }) {
if (dismissed) {
return null;
}
return <div className="banner">Some announcement</div>;
}This isn't an error state or a special case to work around — it's a first-class, ordinary outcome, and the component will happily render something again the next time its props or state say it should.
Try it yourself
This component exercises all three patterns: an early return, a ternary, and
a correctly-guarded &&.
What to remember
- Ternaries handle either/or;
&&handles "render this, or nothing." - Guard
&&with a real boolean comparison (count > 0), not a raw number or string, or a falsy-but-nonzero-looking value like0renders as visible text. - For several distinct states, an early return outside the JSX is clearer than nested ternaries.
- Returning
null(orfalse/undefined) is the normal, first-class way to render nothing.
Check yourself
4 questions · pass 3/4 to unlock Handling Events
1.Why can't you write
if (isLoading) { return <Spinner />; } else { return <Content />; }directly inside JSX curly braces?2.What does
count && <Badge count={count} />render whencountis0?3.What does returning
nullfrom a component do?4.Which fixes the
count && <Badge />stray-zero problem?
4 left to answer