Skip to main content

Conditional Rendering

Conditional Rendering

Written by Published

There is no if inside JSX, because JSX takes expressions. Every technique here is a way of producing a value that is either some UI or nothing.

The three tools

Almost all conditional React is one of three shapes, and picking the right one is mostly about how many branches you have.

  1. Early return - the whole component renders something different, or nothing.
  2. Ternary - two alternatives in one spot.
  3. && - one thing or nothing.

All three, in one component

jsx

function Status({ state, count }) {
  // 1. Early return: nothing to show at all.
  if (state === "hidden") return null

  return (
    <div>
      {/* 2. Ternary: one of two things */}
      <p>{state === "busy" ? "Working…" : "Ready"}</p>

      {/* 3. && : something or nothing */}
      {count > 0 && <p>{count} pending</p>}
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <div>
    <Status state="busy" count={3} />
    <Status state="ready" count={0} />
    <Status state="hidden" count={9} />
    <p style={{ color: "#64748b" }}>Three renders above; the third produced nothing.</p>
  </div>
)

The && trap

&& returns its left value when that value is falsy - it does not return false. And 0 is falsy but React renders it, so {items.length && <List />} puts a bare 0 on the page when the list is empty.

Where the stray zero comes from

jsx

function Broken({ items }) {
  // items.length is 0, so this renders 0 — not nothing.
  return <div>Broken: [{items.length && <span>has items</span>}]</div>
}

function Fixed({ items }) {
  // A real boolean on the left, and false renders nothing.
  return <div>Fixed: [{items.length > 0 && <span>has items</span>}]</div>
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <div>
    <Broken items={[]} />
    <Fixed items={[]} />
  </div>
)

The rule: put a real boolean on the left of &&. Compare with > 0, or use Boolean(...), or use a ternary with an explicit null.

When to use an early return

If a condition changes the whole output, return early rather than wrapping the entire body in a ternary. It keeps the main path at the left margin and reads as a list of cases.

Guard clauses read better

jsx

function Profile({ user, error }) {
  if (error) return <p style={{ color: "#b91c1c" }}>Could not load.</p>
  if (!user) return <p>Loading…</p>

  // The interesting path is not nested inside anything.
  return (
    <div>
      <strong>{user.name}</strong>
      <p>{user.role}</p>
    </div>
  )
}

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <div>
    <Profile error={true} />
    <Profile user={null} />
    <Profile user={{ name: "Ada", role: "Engineer" }} />
  </div>
)

Keeping it readable

  • Never nest ternaries. Two levels is already hard to read; three is unmaintainable.
  • More than two branches: compute above the return, or use a lookup object.
  • Extract a component when a branch grows past a few lines.
  • {condition ? : null} is fine and often clearer than &&.