🎓 What You Will Learn
- The && operator: render something only when a condition is truthy
- Ternaries: pick between two pieces of UI inline
- Early returns: handle loading/error/empty states with guard clauses
- Object lookup / switch: map a value to UI without an if/else chain
- The falsy gotcha: why
{count && <p>...</p>}can render a stray0 - Rendering nothing: using
nullon purpose
Why Conditional Rendering Matters
Almost every real component needs to show different UI depending on state: a spinner while data loads, an error message when a request fails, a login button vs. a profile menu. In plain HTML you'd need separate pages. In React, it's just JavaScript — if,&&, and ternaries decide what gets rendered.
There's no special "React conditional syntax" to memorize. JSX is just JavaScript expressions wrapped in curly braces, so any expression that evaluates to a React node (or null) can be rendered.
The && Operator — Render When True
The most common pattern: render something only if a condition is truthy, and render nothing otherwise. JavaScript's && short-circuits — if the left side is falsy, it returns the left side without evaluating the right side.
function Notification({ hasUnread }) {
return (
<div>
{/* If hasUnread is truthy, render the badge. Otherwise render nothing */}
{hasUnread && <span className="badge">New</span>}
</div>
);
}Nothing to show yet — try the buttons above.
The Ternary — Choosing Between Two Outcomes
When you need to show one thing or another (not "something or nothing"), reach for the ternary operator: condition ? ifTrue : ifFalse.
function LoginButton({ loggedIn, onClick }) {
return (
<button onClick={onClick}>
{loggedIn ? "Log out" : "Log in"}
</button>
);
}&& for "show this or show nothing." Use a ternary for "show this or show that." Reaching for the wrong one is usually what makes conditional JSX hard to read.Early Returns — Guard Clauses for Bigger Components
Once a component has to handle loading, error, empty, and success states, nested ternaries inside JSX turn into a wall of ? :. The cleaner fix: return early from the function before you even get to the main JSX.
// ❌ Gets hard to read fast — nested ternaries
return (
<div>
{status === "loading" ? (
<Spinner />
) : status === "error" ? (
<ErrorMessage />
) : users.length === 0 ? (
<EmptyState />
) : (
<UserList users={users} />
)}
</div>
);- Alice
- Bob
- Charlie
if block, and the "happy path" JSX at the bottom of the function only has to deal with the success case.Object Lookup — Replacing a Long if/else Chain
When a value maps to one of several known outcomes (an order status, a user role, an HTTP error code), an if/else chain or switch works, but a plain object lookup is usually shorter and easier to extend — add one more key, not one more branch.
switch statement is clearer than cramming logic into an object lookup.The Falsy Gotcha — Why You See a Stray 0
&& returns whichever side it stops at. If the left side is 0, && returns 0 — and React renders 0, because 0 is a valid, visible value (unlike false, null, or undefined, which React silently skips).
function CartSummary({ cartCount }) {
return (
<div>
{/* ❌ If cartCount is 0, this renders a literal "0" on the page */}
{cartCount && <p>{cartCount} items in cart</p>}
{/* ✅ Force a real boolean first */}
{cartCount > 0 && <p>{cartCount} items in cart</p>}
{/* ✅ Or use a ternary, which always resolves to one branch */}
{cartCount > 0 ? <p>{cartCount} items in cart</p> : null}
</div>
);
}{items.length && <List items={items} />} renders a bare 0 when the list is empty. Always compare with > 0, !== 0, or convert with Boolean(...) before using && on a number.Rendering Nothing on Purpose
Sometimes the correct output is nothing. Returning null from a component tells React "render no DOM here" — it's not an error, it's a valid, intentional choice.
function Banner({ dismissed, message }) {
if (dismissed) {
return null; // Nothing rendered — perfectly valid
}
return <div className="banner">{message}</div>;
}Common Mistakes
// ❌ BAD: renders "0" when count is 0
{count && <Badge count={count} />}
// ✅ GOOD: always resolves to a boolean
{count > 0 && <Badge count={count} />}// ❌ BAD: unreadable past 2 branches
{a ? <A /> : b ? <B /> : c ? <C /> : <D />}
// ✅ GOOD: early returns, or an object/switch lookup
if (a) return <A />;
if (b) return <B />;
if (c) return <C />;
return <D />;// ❌ BAD: no "error" branch — errors silently render nothing
if (status === "loading") return <Spinner />;
return <UserList users={users} />;
// ✅ GOOD: every known state is handled explicitly
if (status === "loading") return <Spinner />;
if (status === "error") return <ErrorMessage />;
return <UserList users={users} />;Best Practices
count > 0) before &&Pattern Reference
| Pattern | Use When | Example |
|---|---|---|
| && | Show something or nothing | {isAdmin && <AdminPanel />} |
| Ternary | Show one of two things | {loggedIn ? <Menu /> : <LoginBtn />} |
| Early return | 3+ distinct UI states | if (loading) return <Spinner />; |
| Object lookup | Map a value to UI/config | STATUS_MAP[status] |
| Return null | Intentionally render nothing | if (hidden) return null; |
Practice Project
Build a small notification center component that:
- Shows a "You're all caught up!" message when there are 0 notifications
- Shows a badge with the count when there's 1 or more (watch the falsy gotcha!)
- Uses early returns for
loadinganderrorstates - Uses an object lookup to render a different icon per notification type
What's Next?
- React Lists: combine conditional rendering with
.map()for empty/filtered states - useState: drive these conditions from real component state
- useEffect: fetch data and manage the loading/error/success states shown here
- React Hooks Deep Dive: custom hooks for reusable loading/error logic
Keep practicing! 💪 Conditional rendering is just JavaScript — the more comfortable you get with &&, ternaries, and early returns, the more readable every component you write becomes.