| |

React 22 ⚛️ Conditional Rendering with Logical AND Operator

The ternary operator answers an either/or question. The logical AND operator answers a simpler one: show this, or show nothing. In React, && is the most compact way to express that pattern. It appears in nearly every codebase—render a badge when a count is positive, show a warning when a form is invalid, display a spinner while data loads. It is short, readable, and idiomatic. It is also the source of one of React’s most famous bugs: the stray 0 that appears on screen when a number is used as the left operand.

This chapter covers conditional rendering with the logical AND operator. You will learn how short-circuit evaluation works in JavaScript, why it fits naturally into JSX, and what values React renders when the expression evaluates to something unexpected. You will see the 0 bug in detail, understand why it happens, and learn the fixes. You will also learn when && is the right choice and when a ternary or an early return is clearer.

By the end, you will understand not just the syntax but the semantics—what && actually returns, what React does with that value, and how to avoid the traps that catch developers who treat && as if it were a boolean operator.

Key point: The && operator does not return true or false. It returns one of its operands. If the left operand is falsy, && returns that falsy value, not false. React renders whatever value the expression produces, which is why 0 and empty strings can appear on screen.


Why logical AND exists in React

The show-or-nothing problem. Many conditional rendering needs are not either/or. They are “show this when the condition is true, and show nothing otherwise.” A badge that appears only when there are unread messages. A warning that appears only when a field is invalid. A tooltip that appears only on hover. The ternary can express this as condition ? <Component /> : null, but that is verbose. The && operator expresses it directly: condition && <Component />. When the condition is falsy, the expression evaluates to the condition itself, and React renders nothing for falsy values like false, null, and undefined.

The short-circuit behavior. JavaScript’s && operator short-circuits: if the left operand is falsy, the right operand is never evaluated. This means condition && <ExpensiveComponent /> does not render or even create the component when condition is false. The performance benefit is real but usually minor. The conceptual benefit is larger: the right side only exists when the left side is truthy, which matches the intent of conditional rendering.

The 0 trap. The short-circuit behavior has a consequence that surprises developers. 0 && <Component /> evaluates to 0, because 0 is falsy and && returns the left operand without evaluating the right. React renders 0 as text on the screen. This is not a bug in React; it is the documented behavior of &&. But it is a bug in application code, and it appears frequently because counts, indices, and array lengths are natural conditions that are also numbers.

The readability problem. && is compact and readable for simple cases. It becomes less readable when the condition is complex or when the right side is a multi-line JSX block. In those cases, a ternary or an early return may be clearer. The && operator is a tool, not a universal solution.

The React philosophy. React does not add special syntax for conditional rendering. It relies on JavaScript’s existing operators. This means React developers must understand JavaScript’s type coercion and truthiness rules. The && operator is a JavaScript operator, not a React feature, and its behavior in React is the same as its behavior anywhere else.


a. Basic syntax and short-circuit evaluation

The && operator takes two operands. It evaluates the left operand first. If the left operand is falsy, it returns that value and does not evaluate the right operand. If the left operand is truthy, it evaluates and returns the right operand.

function Notification({ hasUnread }) {
  return (
    <div>
      {hasUnread && <span className="badge">New messages</span>}
    </div>
  );
}

When hasUnread is true, the expression evaluates to the <span> element, and React renders it. When hasUnread is false, the expression evaluates to false, and React renders nothing for false.

React renders nothing for several falsy values: false, null, undefined, and true (wait—true is truthy, but React renders nothing for true specifically because booleans are ignored as children). The values React does render include 0 and "" (empty string). This is the root of the 0 bug.

The short-circuit behavior means the right side is not evaluated when the left is falsy. This is useful when the right side has side effects or is expensive to compute, though in JSX the right side is usually a component element, which is cheap to create.


b. The 0 bug and its fixes

The 0 bug occurs when the left operand of && is the number 0. Since 0 is falsy, && returns 0, and React renders 0 as text.

function ItemCount({ count }) {
  return (
    <div>
      {count && <p>You have {count} items.</p>}
      {/* When count is 0, renders "0" on screen */}
    </div>
  );
}

There are several fixes, each appropriate in different situations.

Convert to boolean. Use a comparison that produces a boolean:

{count > 0 && <p>You have {count} items.</p>}

This is the most common fix. count > 0 is either true or false, and React renders nothing for false.

Use a ternary with null. This is more verbose but explicit:

{count ? <p>You have {count} items.</p> : null}

Wait—this still has the bug, because count is 0 and the ternary’s condition is falsy. The fix is to make the condition a boolean:

{count > 0 ? <p>You have {count} items.</p> : null}

Double negation. Convert the value to a boolean with !!:

{!!count && <p>You have {count} items.</p>}

This works but is less readable than count > 0.

Use a helper or guard. For components that may receive 0, guard at the top:

function ItemCount({ count }) {
  if (!count) return null;
  return <p>You have {count} items.</p>;
}

This is the clearest fix when the component exists only to render the conditional content.

The 0 bug is not limited to 0. Empty strings have the same problem: "" && <Component /> evaluates to "", and React renders nothing for an empty string—actually, React renders nothing for empty string, so this is not a bug. The bug is specifically with numbers and NaN. A string condition is safe because empty strings render as nothing.


c. When to use && versus ternary

The choice between && and the ternary depends on whether there are two branches or one.

Use && when one branch is “render nothing.”

{isLoading && <Spinner />}
{hasError && <ErrorBanner />}
{showTooltip && <Tooltip />}

These read naturally as “when X, render Y.”

Use the ternary when both branches have content.

{isLoading ? <Spinner /> : <Content />}
{user ? <Profile /> : <LoginPrompt />}

The ternary makes both branches explicit.

Use && with a boolean condition when the condition may be a number.

{count > 0 && <Badge count={count} />}

Never write {count && <Badge />} when count could be 0.

Use early returns for multi-branch logic.

if (isLoading) return <Spinner />;
if (error) return <Error />;
return <Content />;

This is clearer than nested ternaries or a chain of && operators.

A useful rule of thumb: if the condition is a boolean, && is fine. If the condition is a number, a string, or an object, convert to boolean first or use a ternary. The conversion makes the intent explicit and avoids the rendering surprises.


Complete Example Session

// ============================================
// PART 1: BASIC &&
// ============================================
function Notification({ hasUnread }) {
  return (
    <div>
      {hasUnread && <span className="badge">New</span>}
    </div>
  );
}
// ============================================
// PART 2: && WITH COMPONENTS
// ============================================
function Dashboard({ isLoading }) {
  return (
    <div>
      {isLoading && <Spinner />}
      <Content />
    </div>
  );
}
// ============================================
// PART 3: THE 0 BUG
// ============================================
function ItemCount({ count }) {
  return (
    <div>
      {count && <p>You have {count} items.</p>}
      {/* count = 0 renders "0" */}
    </div>
  );
}
// ============================================
// PART 4: FIX WITH BOOLEAN COMPARISON
// ============================================
function ItemCountFixed({ count }) {
  return (
    <div>
      {count > 0 && <p>You have {count} items.</p>}
    </div>
  );
}
// ============================================
// PART 5: FIX WITH TERNARY
// ============================================
function ItemCountTernary({ count }) {
  return (
    <div>
      {count > 0 ? <p>You have {count} items.</p> : null}
    </div>
  );
}
// ============================================
// PART 6: FIX WITH EARLY RETURN
// ============================================
function ItemCountEarly({ count }) {
  if (!count) return null;
  return <p>You have {count} items.</p>;
}
// ============================================
// PART 7: && WITH STRING CONDITION (SAFE)
// ============================================
function Warning({ message }) {
  return (
    <div>
      {message && <div className="warning">{message}</div>}
      {/* Empty string renders nothing safely */}
    </div>
  );
}
// ============================================
// PART 8: && WITH MULTIPLE ELEMENTS
// ============================================
function LoadingBlock({ isLoading }) {
  return (
    <div>
      {isLoading && (
        <>
          <Spinner />
          <p>Loading...</p>
        </>
      )}
    </div>
  );
}
// ============================================
// PART 9: CHAINED &&
// ============================================
function Form({ isDirty, isValid }) {
  return (
    <div>
      {isDirty && isValid && <button>Save</button>}
      {/* Both conditions must be true */}
    </div>
  );
}
// ============================================
// PART 10: && VERSUS TERNARY
// ============================================
function Status({ isLoading, data }) {
  return (
    <div>
      {isLoading && <Spinner />}
      {!isLoading && data && <Content data={data} />}
      {/* Or use ternary for the second: */}
      {/* {isLoading ? <Spinner /> : <Content data={data} />} */}
    </div>
  );
}

The ten parts covered the essential patterns: basic &&, && with components, the 0 bug, three fixes (boolean comparison, ternary, early return), && with string conditions, && with fragments, chained &&, and a comparison with the ternary.


Quick Reference

&& Behavior

Left OperandResultReact Renders
trueRight operandRight operand
falsefalseNothing
000
""""Nothing
nullnullNothing
undefinedundefinedNothing
NaNNaNNaN
"text"Right operandRight operand
1Right operandRight operand

The 0 Bug Fixes

FixExampleNotes
Boolean comparisoncount > 0 && <C />Most common
Ternary with nullcount > 0 ? <C /> : nullExplicit
Double negation!!count && <C />Less readable
Early returnif (!count) return null;Clearest for components
Boolean castBoolean(count) && <C />Explicit

&& vs Ternary

Aspect&&Ternary
BranchesOne (show or nothing)Two
0 bugYesNo (if condition is boolean)
CompactnessHigherLower
ExplicitnessLowerHigher
Best forBoolean conditionsEither/or choices

Safe Conditions for &&

Condition TypeSafe?Notes
Boolean variableYesisLoading && <Spinner />
ComparisonYescount > 0 && <Badge />
String (non-empty)Yesmessage && <Warning />
NumberNocount && <Badge /> renders 0
NaNNoRenders NaN
ObjectUsuallyObjects are truthy; null is safe

Best Practices

✅ Do This:

// Use boolean conditions with &&
{isLoading && <Spinner />}                                 // ✅

// Convert numbers to boolean comparisons
{count > 0 && <Badge count={count} />}                     // ✅

// Use fragments for multiple elements
{isLoading && (<><Spinner /><Text /></>)}                  // ✅

// Use early return when the component is the conditional
function Badge({ count }) {
  if (!count) return null;
  return <span>{count}</span>;
}                                                          // ✅

// Wrap multi-line JSX in parentheses
{show && (
  <div>
    <p>Content</p>
  </div>
)}                                                         // ✅

❌ Don’t Do This:

// Use numbers directly with &&
{count && <Badge />}                                       // ❌ (renders 0)

// Use NaN-producing expressions
{items.length / 0 && <Text />}                             // ❌ (renders NaN)

// Forget parentheses for multi-line JSX
{show && 
  <div>
    <p>Content</p>
  </div>
}                                                          // ❌ (parsing issues)

// Use && when both branches have content
{isLoading && <Spinner />}                                 // ❌ if you need a fallback
// use ternary instead

// Chain too many conditions
{a && b && c && d && <Component />}                        // ⚠️ (hard to read)

Common Pitfalls

PitfallWhy It HappensFix
0 appears on screenNumber used as left operandUse count > 0
NaN appears on screenNaN used as left operandUse !isNaN(count) &&
Parsing errorMulti-line JSX without parensWrap in (...)
Nothing rendersCondition is falsyCheck condition value
Empty string rendersActually renders nothingSafe—no fix needed
Chained && unreadableToo many conditionsExtract to a variable

Real-World Examples

1. Loading Spinner

{isLoading && <Spinner />}

2. Error Banner

{error && <ErrorBanner message={error} />}

3. Unread Badge

{unreadCount > 0 && <Badge count={unreadCount} />}

4. Empty State Message

{items.length === 0 && <p>No items found.</p>}

5. Admin Controls

{user.isAdmin && <AdminPanel />}

6. Form Validation Message

{!isValid && touched && <p className="error">Invalid input</p>}

7. Tooltip on Hover

{isHovered && <Tooltip text={text} />}

8. Conditional Class

<div className={isActive ? 'active' : ''} />

9. Save Button

{isDirty && <button onClick={save}>Save</button>}

10. Multiple Conditional Blocks

{isLoading && <Spinner />}
{error && <Error />}
{data && <Content data={data} />}

Visual

Short-Circuit Evaluation

┌─────────────────────────────────────────────────────────────┐
│  HOW && EVALUATES                                           │
│                                                             │
│  condition && <Component />                                 │
│                                                             │
│  Step 1: Evaluate condition                                 │
│    │                                                        │
│    ├── Falsy ──▶ Return condition (do NOT evaluate right)   │
│    │              │                                          │
│    │              └── React renders the returned value      │
│    │                                                        │
│    └── Truthy ──▶ Evaluate and return right operand         │
│                   │                                          │
│                   └── React renders <Component />           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

The 0 Bug Explained

┌─────────────────────────────────────────────────────────────┐
│  WHY 0 RENDERS                                              │
│                                                             │
│  count = 0                                                  │
│                                                             │
│  count && <p>You have {count} items.</p>                    │
│                                                             │
│  JavaScript:                                                │
│    0 is falsy → && returns 0                                │
│    Right side not evaluated                                 │
│                                                             │
│  JSX receives: 0                                            │
│  React renders: 0                                           │
│                                                             │
│  Screen shows: "0"                                          │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  FIXES                                                      │
│                                                             │
│  count > 0 && <p>...</p>      → condition is boolean        │
│  count ? <p>...</p> : null    → still buggy!                │
│  count > 0 ? <p>...</p> : null → fixed                      │
│  !!count && <p>...</p>        → fixed but less clear        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

React’s Rendering of Falsy Values

┌─────────────────────────────────────────────────────────────┐
│  WHAT REACT RENDERS FOR FALSY VALUES                        │
│                                                             │
│  Value         Rendered?                                    │
│  ─────────────────────────                                  │
│  false         Nothing                                      │
│  null          Nothing                                      │
│  undefined     Nothing                                      │
│  true          Nothing (booleans ignored)                   │
│  0             Renders "0"                                  │
│  -0            Renders "0"                                  │
│  NaN           Renders "NaN"                                │
│  ""            Nothing                                      │
│  "text"        Renders "text"                               │
│  1             Renders "1"                                  │
│                                                             │
│  Only 0, -0, and NaN are the problematic falsy values.      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Decision: && vs Ternary

┌─────────────────────────────────────────────────────────────┐
│  CHOOSING BETWEEN && AND TERNARY                            │
│                                                             │
│  Does the false case render something?                      │
│    │                                                        │
│    ├── YES ──▶ Ternary: cond ? <A /> : <B />                │
│    │                                                        │
│    └── NO                                                   │
│          │                                                  │
│          ├── Is the condition a boolean?                    │
│          │     ├── YES ──▶ && : cond && <A />               │
│          │     └── NO ──▶ Ternary with null                 │
│          │                or boolean comparison             │
│          │                                                  │
│          └── Is the JSX multi-line?                         │
│                ├── YES ──▶ Either works; ternary may be     │
│                │            clearer                         │
│                └── NO ──▶ && is most compact                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Summary

ItemValue
Operatorcondition && <Component />
BehaviorReturns left operand if falsy, right if truthy
React rendersThe returned value
Safe conditionsBooleans, comparisons, non-empty strings
Unsafe conditionsNumbers, NaN
0 bug0 && <C /> renders 0
Fixcount > 0 && <C />
AlternativeTernary with null
Multi-line JSXWrap in parentheses
Multiple conditionsa && b && <C />

Key takeaways:

  • && is for show-or-nothing. When the false case renders nothing, && is the most compact expression. When both cases render something, use a ternary.
  • && returns an operand, not a boolean. This is the source of the 0 bug. 0 && <Component /> returns 0, and React renders 0 on screen.
  • React renders nothing for false, null, undefined, and true. It renders 0 and NaN as text. Empty strings render nothing, which makes string conditions safe.
  • Convert numbers to boolean comparisons. Use count > 0 or items.length > 0 as the left operand. This produces a boolean and avoids the 0 bug.
  • Early returns are the clearest fix when the component exists for the conditional. If the entire component renders one thing or nothing, if (!condition) return null; at the top is the most readable form.
  • Chained && works but becomes hard to read. a && b && c && <Component /> requires all conditions to be true. When the chain grows, extract the condition to a variable with a descriptive name.
  • Wrap multi-line JSX in parentheses. {show && ( followed by JSX and )} is the convention. Without parentheses, the parser may misinterpret the expression.

Remember: The logical AND operator is the most idiomatic way to express “render this only when the condition is true.” It is compact, readable, and works with any boolean condition. The 0 bug is not a flaw in the operator; it is a consequence of JavaScript’s truthiness rules combined with React’s rendering behavior. The fix is simple: make the condition a boolean. When the logic is more complex than a single condition, or when both branches render something, reach for a ternary or an early return. Use the simplest tool that expresses the intent clearly, and guard against the number-shaped conditions that produce the stray 0.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!