ReactTernary & Logical && in JSX

Ternary & Logical && in JSX

React JSX is just JavaScript, which means you can use all of JavaScript's inline expression operators to decide what renders. The two you'll use constantly are the ternary operator ? : and the logical AND &&. Understanding each one's behaviour — including their edge cases — is essential for writing correct, readable React.

The Ternary Operator: Two-Way Branches

The ternary condition ? ifTrue : ifFalse is the primary tool for choosing between two JSX outputs. Both branches are expressions, so the ternary can live directly inside JSX:

JSX
// Simple text choice
function SaveButton({ isSaving }) {
  return (
    <button disabled={isSaving}>
      {isSaving ? 'Saving...' : 'Save'}
    </button>
  )
}

// Element choice
function Alert({ type, message }) {
  return (
    <div className={type === 'error' ? 'alert-error' : 'alert-info'}>
      {type === 'error'
        ? <span>⚠ Error: {message}</span>
        : <span>ℹ Info: {message}</span>
      }
    </div>
  )
}

// Conditional rendering of a whole section
function UserCard({ user }) {
  return (
    <div>
      <h2>{user.name}</h2>
      {user.isAdmin
        ? <span className="badge">Administrator</span>
        : <span className="badge">Member</span>
      }
    </div>
  )
}
Ternary with null for "Nothing"

Use null as one branch of the ternary when you want to render nothing for the false case:

JSX
function ErrorMessage({ error }) {
  return (
    <div>
      {error ? <p className="error">{error}</p> : null}
    </div>
  )
}

// Equivalent — but && is more concise for single-branch cases
function ErrorMessage2({ error }) {
  return (
    <div>
      {error && <p className="error">{error}</p>}
    </div>
  )
}
The && Operator: Single-Branch Show/Hide

The logical AND && is JavaScript's "short-circuit" operator. If the left side is truthy, it returns the right side. If the left side is falsy, it returns the left side without evaluating the right side.

In JSX, returning false, null, or undefined renders nothing — so && becomes an elegant "show this element if condition is true":

JSX
function Inbox({ messages, hasNewMail }) {
  return (
    <div>
      <h2>Inbox</h2>
      {hasNewMail && <span className="badge">New!</span>}
      {messages.length > 0 && (
        <ul>
          {messages.map(msg => (
            <li key={msg.id}>{msg.subject}</li>
          ))}
        </ul>
      )}
    </div>
  )
}
The 0 Gotcha — The Most Common && Bug

Here's the most dangerous && pitfall in React. When the left side evaluates to 0, JavaScript's && returns 0 — and React renders 0 to the DOM as a text node, which displays the digit "0" on screen.

JSX
// ✗ BUG: renders the number "0" when count is 0
function MessageBadge({ count }) {
  return (
    <span>
      {count && <Badge>{count}</Badge>}
    </span>
  )
}
// With count=0: renders <span>0</span> — "0" appears on screen!

// ✓ Fix 1: coerce to boolean
{count > 0 && <Badge>{count}</Badge>}

// ✓ Fix 2: Boolean()
{Boolean(count) && <Badge>{count}</Badge>}

// ✓ Fix 3: double negation
{!!count && <Badge>{count}</Badge>}

// ✓ Fix 4: ternary (explicit null for falsy branch)
{count ? <Badge>{count}</Badge> : null}
Warning
The `0 &&` bug is so common that it's worth memorizing the fix. Any time the left side of `&&` could be a number, use `count > 0 &&` or a ternary instead. Other falsy primitives (`false`, `null`, `undefined`) are safe because React doesn't render them to the DOM.
The || Operator: Fallback Values

The logical OR || returns the first truthy value. It's useful for displaying fallback content when a value is falsy:

JSX
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.displayName || 'Anonymous'}</h2>
      <p>{user.bio || 'No bio yet.'}</p>
      {/* Careful: || treats "" (empty string) and 0 as falsy too */}
    </div>
  )
}

// user.displayName = ''  → shows 'Anonymous' (treats empty string as falsy)
// user.displayName = null → shows 'Anonymous'
// user.displayName = 'Sarah' → shows 'Sarah'
The ?? Operator: Null/Undefined Fallback

The nullish coalescing operator ?? is like || but only triggers for null and undefined — not for 0, "", or false:

JSX
function ProductCard({ product }) {
  return (
    <div>
      {/* ?? is better here: a price of 0 is valid! */}
      <span>${product.price ?? 'Price unavailable'}</span>

      {/* || would wrongly show "Price unavailable" for price=0 */}
      <span>${product.price || 'Price unavailable'}</span>
    </div>
  )
}

// product.price = 0    → ?? shows "$0", || shows "Price unavailable"
// product.price = null → ?? shows "Price unavailable", || same
// product.price = 9.99 → both show "$9.99"
Nested Ternaries: When They Get Unreadable

Ternaries can be nested for multi-branch logic — but they quickly become impossible to read. Here's the inflection point:

JSX
// ✗ Hard to read — three-level nested ternary
function TrafficLight({ status }) {
  return (
    <div className={
      status === 'red' ? 'light-red'
      : status === 'yellow' ? 'light-yellow'
      : status === 'green' ? 'light-green'
      : 'light-off'
    } />
  )
}

// ✓ Much clearer — extract to a lookup object or variable
const COLOR_CLASS = {
  red: 'light-red',
  yellow: 'light-yellow',
  green: 'light-green',
}

function TrafficLight({ status }) {
  const className = COLOR_CLASS[status] ?? 'light-off'
  return <div className={className} />
}

// ✓ Or extract with a dedicated function
function getStatusClass(status) {
  if (status === 'red') return 'light-red'
  if (status === 'yellow') return 'light-yellow'
  if (status === 'green') return 'light-green'
  return 'light-off'
}

function TrafficLight({ status }) {
  return <div className={getStatusClass(status)} />
}
Extracting Conditions to Variables

When JSX gets cluttered with inline conditions, move the logic above the return:

JSX
function DashboardHeader({ user, notifications, isMobile }) {
  // Compute derived values before the JSX
  const isAdmin = user?.role === 'admin'
  const hasUnread = notifications.filter(n => !n.read).length > 0
  const greeting = user ? `Welcome back, ${user.name}` : 'Welcome'

  return (
    <header>
      <h1>{greeting}</h1>
      {isAdmin && <span className="admin-badge">Admin</span>}
      {hasUnread && <NotificationDot />}
      {!isMobile && <SearchBar />}
    </header>
  )
}
Optional Chaining in JSX

The optional chaining operator ?. pairs well with && and ?? when accessing deeply nested data:

JSX
function PostCard({ post }) {
  return (
    <article>
      <h2>{post.title}</h2>
      {post.author?.avatar && (
        <img src={post.author.avatar} alt={post.author.name ?? 'Author'} />
      )}
      <p>{post.content?.slice(0, 200) ?? 'No content'}</p>
    </article>
  )
}
  • && — use for single-branch show/hide; always guard number values with > 0

  • ? : — use for two-branch choices; prefer over && with null

  • || — use for string fallbacks when 0 and "" should trigger the fallback

  • ?? — use for fallbacks when 0 and "" are valid values (only null/undefined triggers it)

  • Nested ternaries — avoid beyond one level; extract to variables or functions

Note
Optional chaining (`?.`) doesn't render anything by itself, but it safely short-circuits to `undefined` when a property doesn't exist. Combine it with `??` for safe access plus fallback: `user?.profile?.bio ?? 'No bio'`.
Tip
When you find yourself writing a complex conditional inside JSX, ask: "Would this be clearer as a variable above the return?" If yes, extract it. The JSX should describe structure, not business logic.