Guard Rendered JSX With A Boolean

ID

javascript.jsx_conditional_render_non_boolean

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

jsx, react, reliability

Description

Reports the {value && <Element />} idiom when the guard is a number rather than a condition. React renders the guard’s falsy value, and a falsy number is a visible 0.

Rationale

&& in JavaScript does not return a boolean: it returns its left operand when that operand is falsy. In JSX children the returned value is then rendered, and React renders every number it is given — including 0 and NaN.

So a guard on a count works for every count but the one that matters: with an empty list the page shows a bare 0 where the list should be. It is the most-reported React rendering bug there is, and it survives review because the line reads as a condition.

Only guards this scanner can see the type of are reported: a numeric literal, and a read of .length or .size, whose type is fixed by the language whatever the receiver is. A plain identifier is left alone — count may well be a number, but the rule would then be guessing, and a wrong guess here lands on correct code. A string literal guard is reported too, for a different reason: its truthiness is constant, so the guard is not a guard.

The check applies to a guard whose value reaches JSX children, which is where it would be rendered. The same expression in an attribute or in plain JavaScript is ordinary code. Chained guards nest to the right, and each && in the chain is judged on its own left operand, so the numeric one is found wherever in the chain it sits.

<div>{items.length && <List items={items} />}</div>    // FLAW - an empty list renders "0"
<div>{selected.size && <Toolbar />}</div>              // FLAW - same for a Set or a Map
<div>{0 && <List />}</div>                             // FLAW - renders "0", always

<div>{items.length > 0 && <List items={items} />}</div> // OK - a comparison is a boolean
<div>{!!items.length && <List items={items} />}</div>  // OK - coerced
<div>{isOpen && <Toolbar />}</div>                     // OK - reads as a condition
<div>{items.length ? <List /> : null}</div>            // OK - a conditional picks a branch

Remediation

Make the guard a boolean. Compare the count (items.length > 0), coerce it (!!items.length), or switch to a conditional expression, which picks a branch instead of returning the guard (items.length ? <List /> : null). The comparison is usually the clearest of the three, because it says what the empty case is.

© 2026 Xygeni. All rights reserved