Conditional rendering patterns in React
Patterns: `&&` for show-or-hide, ternary for either-or, early return for guards, switch/lookup for many cases, render prop for conditional content with shared logic, Suspense/ErrorBoundary for loading/error. Avoid `&&` with a numeric `0` (renders "0"). Hoist branches into named components when JSX gets dense.
Common patterns
1. Logical AND
{user && <Greeting name={user.name} />}Renders when truthy. Gotcha: {count && <X />} renders "0" when count is 0. Use count > 0 && ... or Boolean(count) && ....
2. Ternary
{user ? <Greeting /> : <SignIn />}Either-or. Don't nest ternaries — pull into components.
3. Early return / guard
function Profile({ user }) {
if (!user) return <SignIn />;
if (user.banned) return <Banned />;
return <Dashboard user={user} />;
}Cleaner than nested ternaries; reads top-down.
4. Lookup table for many cases
const STATUS_VIEW = {
idle: <Idle />,
loading: <Skeleton />,
error: <Error />,
success: <Result />,
};
return STATUS_VIEW[status] ?? null;Beats a 4-way ternary or nested if.
5. Switch returning JSX
function Status({ s }) {
switch (s) {
case "loading": return <Skeleton />;
case "error": return <Error />;
case "success": return <Result />;
default: return null;
}
}6. Suspense + ErrorBoundary
For loading/error of async resources:
<ErrorBoundary fallback={<Error />}>
<Suspense fallback={<Skeleton />}>
<UserData />
</Suspense>
</ErrorBoundary>Cleaner than isLoading ? Skeleton : data ? Render : Error chains.
7. Polymorphic component / render prop
<Auth>
{(user) => user ? <Dashboard /> : <SignIn />}
</Auth>Shares logic, exposes branches.
Anti-patterns
{count && ...}when count can be 0 — renders "0".- Nested ternaries more than 1 level — extract.
- JSX with 6 nested && / ternaries — name the branches.
- Returning null for everything — Suspense / ErrorBoundary read better.
Hoist into components
// Hard to read
{tab === 'a' ? <A /> : tab === 'b' ? <B /> : tab === 'c' ? <C /> : null}
// Better
const VIEWS = { a: A, b: B, c: C };
const View = VIEWS[tab] ?? Fallback;
return <View />;Performance
Conditional rendering removes nodes from the tree (unmounts) — children's state is lost. To preserve state across hide/show, use hidden attribute or display:none (still mounted but invisible) — but loses keyboard focus etc.
Interview framing
"Use && for show-or-hide, ternary for either-or, early return for guards, lookup table for many-case status, Suspense + ErrorBoundary for async loading/error. The {count && ...} gotcha bites — when count is 0, React renders the string "0". Don't nest ternaries — extract into named components or a lookup map. Conditional rendering unmounts; if state needs to survive hide/show, use the hidden attribute or CSS display:none."
Follow-up questions
- •What's the {0 && ...} bug?
- •When would you keep a component mounted but hidden?
- •Compare a switch vs a lookup table.
Common mistakes
- •{count && ...} when count can be 0.
- •Nested ternaries.
- •Mounting/unmounting state-heavy components every toggle.
Performance considerations
- •Unmount/remount loses state. Hidden but mounted preserves state but costs render.
Edge cases
- •Falsy values like '' or 0 in &&.
- •Suspense fallback inside a transition.
- •Keying-based remounts to reset state.
Real-world examples
- •Status displays, tabs, route gates, auth flows.