Lesson 61 of 80
&&, ternaries, and early returns all decide what to render — but each one has its own specific way of quietly rendering the wrong thing.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
JSX doesn't have an if statement, because JSX is an expression, not a template language with its own control-flow syntax — so "conditional rendering" really means "which JavaScript expression do I reach for to decide what shows up." The three common tools are && for "render this or nothing," the ternary ? : for "render this or that," and an early return for "don't render this component's normal output at all." Each is right in a different situation, and each has its own specific failure mode.
&& falsy trapfunction Cart({ items }) {
return (
<div>
<h2>Your Cart</h2>
{items.length && <ItemList items={items} />}
</div>
);
}
This looks reasonable: "if there are items, render the list." But when items.length is 0, && doesn't return false — it returns the first falsy operand as-is, which here is the number 0. React renders false, null, and undefined as nothing, but 0 is a valid, renderable value, so an empty cart shows a literal stray 0 on the page. The fix is to force a real boolean:
{items.length > 0 && <ItemList items={items} />}
The ternary avoids that specific trap since both branches are explicit, but has its own problem — readability collapses once you nest them:
{status === "loading" ? <Spinner /> : status === "error" ? <ErrorMsg /> : status === "empty" ? <EmptyState /> : <ItemList items={items} />}
Technically correct, genuinely hard to scan. Past two branches, an early return in the component body is almost always clearer:
function Cart({ status, items }) {
if (status === "loading") return <Spinner />;
if (status === "error") return <ErrorMsg />;
if (status === "empty") return <EmptyState />;
return <ItemList items={items} />;
}
&& renders any falsy left side that isn't false, null, or undefined. 0, NaN, and "" all slip through and get rendered literally — items.length && ..., count && ..., and errorMessage && ... (an empty string) are all instances of the same bug. Boolean(x) && ... or x > 0 && ... closes the gap.if (!user) return null; placed above a useEffect or useState call means that hook simply doesn't run on renders where user is falsy — and the next render where it becomes truthy calls a different number of hooks than the previous one, which React flags as a rules-of-hooks violation, not a soft warning.null-ish. Returning a string from one branch and an element from the other works, but mixing in something like an array of elements without keys is a much easier way to reintroduce the exact key-related bugs the keys-and-lists lesson warns about.return is often clearer than inlining it, especially once a ternary or early-return chain gets long — const body = status === "loading" ? <Spinner /> : <ItemList items={items} />; followed by return <div>{body}</div>; separates "decide what to show" from "how it's laid out."&& doing exactly what && always does — returning an operand, not a boolean — is the same short-circuit-evaluation behavior from plain JavaScript truthy/falsy rules; JSX just adds the twist that 0 gets rendered while false doesn't, which is a distinction that isn't visible from &&'s ordinary behavior alone.