Skip to content

Conditional Rendering Interview Questions & Answers

15 questions Updated 2026-06-23 Share:

React conditional rendering interview questions — ternary, &&, null, guard clauses, loading states, CSS display vs conditional mounting, and clean multi-condition patterns.

Read the in-depth guideReact Conditional Rendering — A Complete Interview Guide(opens in new tab)
15 of 15

React has four idiomatic patterns:

// 1. Logical && — render right side only when left is truthy
{isLoggedIn && <Dashboard />}

// 2. Ternary — render one of two options
{isLoggedIn ? <Dashboard /> : <Login />}

// 3. Variable / if-else above return
let content = isLoggedIn ? <Dashboard /> : <Login />
return <main>{content}</main>

// 4. Early return — bail out of the component entirely
if (!isLoggedIn) return <Login />
return <Dashboard />

Each has its place:

  • && — one branch only, very readable when the condition is simple.
  • Ternary — exactly two branches.
  • Variable / if-else — complex conditions with more than two outcomes.
  • Early return — guard clause at the top when the component shouldn't render at all in certain states.

Rule of thumb: use the simplest form that's still readable. If a ternary has nested ternaries, it's time for an if-else or a variable.

JavaScript's && returns the last evaluated operand — not necessarily a boolean. React renders whatever value appears on the right side of && when the left side is truthy.

const count = 5
return (
  <div>
    {count > 0 && <Badge count={count} />}
    {/* → renders <Badge /> because (count > 0) is true */}
  </div>
)

When the condition is false, && short-circuits and returns false. React treats false as "render nothing" — no output.

Rule of thumb: {condition && <Element />} is idiomatic React. The condition should be a boolean; if in doubt, cast it: {!!value && <X />}.

&& returns the left operand when it is falsy. If that operand is 0, React renders the number 0 (since 0 is a valid renderable value).

const messages = []    // length is 0

// ❌ Renders "0" when messages is empty
return <div>{messages.length && <MessageList items={messages} />}</div>

// ✅ Force the left side to a boolean
return <div>{messages.length > 0 && <MessageList items={messages} />}</div>

// ✅ Or use Boolean()
return <div>{Boolean(messages.length) && <MessageList items={messages} />}</div>

// ✅ Or use a ternary
return <div>{messages.length ? <MessageList items={messages} /> : null}</div>

Rule of thumb: never use count && or array.length && directly. Always compare: count > 0 && or array.length > 0 &&. The same trap exists for any number that could be 0.

The ternary expression (condition ? a : b) is a JavaScript expression and is legal inside JSX {}. It's the idiomatic way to render one of two alternatives.

function AuthStatus({ isLoggedIn, username }) {
  return (
    <header>
      {isLoggedIn
        ? <span>Welcome, {username}!</span>
        : <a href="/login">Sign in</a>
      }
    </header>
  )
}

Use null for the false branch when you want to render nothing:

{isAdmin ? <AdminPanel /> : null}
// Equivalent to:
{isAdmin && <AdminPanel />}

Rule of thumb: ternaries are great for exactly two outcomes. For more than two, a variable or a helper function is cleaner.

Choose based on the number of outcomes and complexity:

Pattern Best for
&& Single optional element; condition is a simple boolean
Ternary Exactly two alternatives; fits on one or two lines
if/else above return Multiple alternatives; complex conditions; shared logic before branching
Early return Guard clause — component has nothing to show in this state
// && — simple optional
{hasError && <ErrorBanner message={error} />}

// Ternary — two outcomes
{loading ? <Spinner /> : <Content data={data} />}

// if/else — three outcomes
let body
if (loading) body = <Spinner />
else if (error) body = <Error message={error} />
else body = <Content data={data} />
return <main>{body}</main>

// Early return — guard clause
if (!user) return null
return <Profile user={user} />

Rule of thumb: default to simplicity. Reach for && first, ternary for two branches, if/else for three or more. Never nest ternaries more than one level deep.

Return null. React renders nothing to the DOM but the component remains mounted — its useEffect hooks still run and refs still attach.

function Tooltip({ visible, text }) {
  if (!visible) return null
  return <div className="tooltip">{text}</div>
}

Other falsy values behave differently:

  • undefined — React 18+ allows returning undefined from a component (was an error in older versions). Treated like null.
  • false — valid child; renders nothing.
  • 0 — renders the digit 0. Careful!
  • Empty string '' — renders nothing visible but creates a text node.

Rule of thumb: return null (not false or undefined) when you want a component to render nothing — it's explicit and unambiguous.

  • CSS display:none — the element is in the DOM, painted, and React's tree, but invisible. State, refs, effects, and event listeners are all live.
  • Conditional rendering — the element is not in the DOM. When the condition becomes false, React unmounts the component: state is reset, effects clean up, and refs detach.
// CSS approach — DOM node always present, just hidden
<div style={{ display: isVisible ? 'block' : 'none' }}>
  <ExpensiveWidget />   {/* still mounted, effects running */}
</div>

// Conditional — component unmounts when isVisible is false
{isVisible && <ExpensiveWidget />}

Use cases:

  • Prefer conditional rendering when you want fresh state on each appearance.
  • Prefer display:none when mounting/unmounting is expensive (e.g. a heavy widget) or when you must preserve internal state across hides.

Rule of thumb: default to conditional rendering. Use CSS visibility only when remounting is measurably expensive or state preservation is required.

Use a ternary or template literal inside className:

// Ternary — toggle between two class names
<button className={isActive ? 'btn btn-active' : 'btn'}>Click</button>

// Template literal — build up from base + optional modifier
<div className={`card ${isHighlighted ? 'card-highlighted' : ''}`}>…</div>

// For multiple conditional classes, clsx/classnames library is cleaner
import clsx from 'clsx'
<div className={clsx('card', { 'card-highlighted': isHighlighted, 'card-error': hasError })}>
  …
</div>

Avoid complex string concatenation inline — it becomes unreadable quickly.

Rule of thumb: for more than one or two conditional classes, add clsx (or classnames) to the project — it handles undefined/false values cleanly and the API is easy to scan.

Extract the guard logic into a dedicated component or hook to keep the rendering logic clean.

// Simple inline
{user.role === 'admin' && <AdminPanel />}

// Reusable gate component
function Can({ role, children }) {
  const { user } = useAuth()
  return user.role === role ? children : null
}

// Usage
<Can role="admin">
  <DeleteButton />
</Can>

// Or a hook
function usePermission(requiredRole) {
  const { user } = useAuth()
  return user.role === requiredRole
}

const canDelete = usePermission('admin')
{canDelete && <DeleteButton />}

Rule of thumb: keep auth/permission logic out of individual components. A <Can> component or usePermission hook keeps it testable and reusable across the app.

Yes, but a switch statement can't go directly inside JSX. Extract it into a helper function or variable.

function StatusBadge({ status }) {
  function renderBadge() {
    switch (status) {
      case 'success': return <span className="badge green">Success</span>
      case 'error':   return <span className="badge red">Error</span>
      case 'loading': return <Spinner />
      default:        return null
    }
  }

  return <div className="status">{renderBadge()}</div>
}

An object map is often cleaner than switch for this pattern:

const BADGE = {
  success: <span className="badge green">Success</span>,
  error:   <span className="badge red">Error</span>,
  loading: <Spinner />,
}

return <div>{BADGE[status] ?? null}</div>

Rule of thumb: object maps are more idiomatic React than switch statements for rendering; switch is fine when you need fallthrough or break behavior.

Extract the logic to a variable, early return, or helper function before the return.

// ❌ Nested ternary — hard to read
return (
  <div>
    {loading
      ? <Spinner />
      : error
        ? <Error message={error} />
        : data
          ? <DataView data={data} />
          : <Empty />}
  </div>
)

// ✅ Variable approach
let content
if (loading)     content = <Spinner />
else if (error)  content = <Error message={error} />
else if (data)   content = <DataView data={data} />
else             content = <Empty />

return <div>{content}</div>

// ✅ Helper component
function AsyncContent({ loading, error, data }) {
  if (loading) return <Spinner />
  if (error)   return <Error message={error} />
  if (!data)   return <Empty />
  return <DataView data={data} />
}

Rule of thumb: if a ternary needs to be nested, refactor to if/else or extract a component. A long chain of nested ternaries is a refactoring signal, not a clever trick.

The guard-clause / early-return pattern keeps the happy path last:

function UserProfile({ userId }) {
  const { data: user, loading, error } = useUser(userId)

  if (loading) return <Skeleton />
  if (error)   return <ErrorMessage error={error} />
  if (!user)   return null

  // Happy path — no nesting, no clutter
  return (
    <article>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
    </article>
  )
}

This pattern is sometimes called "bail early, render late." Each guard handles one sad path and returns, leaving the main render clean.

Rule of thumb: load state first, error state second, empty/null state third, happy path last. This order prevents accidentally rendering undefined properties.

Short-circuit evaluation means JavaScript stops evaluating an expression as soon as the result is determined:

  • In A && B — if A is falsy, B is never evaluated.
  • In A || B — if A is truthy, B is never evaluated.

React leverages && for conditional rendering because when the condition is false, the right-hand component is not evaluated (and thus not rendered):

// UserMenu is never evaluated when isLoggedIn is false
{isLoggedIn && <UserMenu user={user} />}

// Useful for expensive renders or components that crash on missing data
{user && user.isAdmin && <AdminPanel />}

Rule of thumb: short-circuit with && when you have one optional element and no fallback. Use || for default values, not for conditional rendering — {value || <Fallback />} renders <Fallback /> whenever value is falsy (including 0 and '').

?? returns the right operand only when the left is null or undefined (not for 0, '', or false). This makes it safer than || for default values.

// || treats 0 and '' as falsy — may swallow valid values
<p>Score: {score || 'No score'}</p>
// If score is 0, renders "No score" — wrong!

// ?? treats only null/undefined as "missing"
<p>Score: {score ?? 'No score'}</p>
// If score is 0, renders "0" — correct

// Common in JSX for optional labels, counts, or display values
<h2>{user.displayName ?? user.email}</h2>

Rule of thumb: use ?? for "provide a fallback when the value is missing." Use || only when you intentionally want 0, false, and '' to also trigger the fallback.

React.lazy lets you load a component's code on demand. Suspense provides a fallback to show while the lazy component loads — effectively a built-in conditional-rendering pattern for async code splitting.

import { lazy, Suspense } from 'react'

const AdminPanel = lazy(() => import('./AdminPanel'))

function App({ user }) {
  return (
    <Suspense fallback={<Spinner />}>
      {user.isAdmin && <AdminPanel />}
    </Suspense>
  )
}

When <AdminPanel /> is first rendered, React suspends, the nearest <Suspense> shows <Spinner />, and the bundle chunk loads in the background. On completion React retries the render and shows the panel.

Rule of thumb: combine lazy + Suspense with normal conditional rendering. Suspense handles the loading state of the import; your && or ternary handles the permission or visibility condition.

More ways to practice

The self-quiz is live. Get notified when mock interviews and new question packs drop.

or
Join our WhatsApp Channel