Back to React
React
easy
mid

What are the common 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.

3 min read·~5 min to think through

Common patterns

1. Logical AND

tsx
{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

tsx
{user ? <Greeting /> : <SignIn />}

Either-or. Don't nest ternaries — pull into components.

3. Early return / guard

tsx
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

tsx
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

tsx
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:

tsx
<ErrorBoundary fallback={<Error />}>
  <Suspense fallback={<Skeleton />}>
    <UserData />
  </Suspense>
</ErrorBoundary>

Cleaner than isLoading ? Skeleton : data ? Render : Error chains.

7. Polymorphic component / render prop

tsx
<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

tsx
// 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.

Senior engineer discussion

Seniors choose pattern by readability and reach for Suspense/ErrorBoundary for the loading/error/success trichotomy.

Related questions