react/jsx-no-leaked-render

Disallow && conditions in JSX that can render 0 or NaN

fixable

{count && <List />} evaluates to count when count is falsy. React renders nothing for false, null and undefined, but it does render 0 and NaN, so an empty list shows a stray 0 on the page. In React Native, a number or a string outside <Text> crashes the app.

The rule reports && in JSX braces, in children and in props, unless its left side looks like a boolean: !x or !!x, a comparison, any call such as Boolean(count), or a variable declared as true or false.

--fix rewrites the condition with the first allowed strategy.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"react/jsx-no-leaked-render" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"react/jsx-no-leaked-render" = ["error", {
  validStrategies = ["ternary", "coerce"],
}]

Examples#

example.jsx2 problems
function Cart({ items, note }) {
  return (
    <Panel footer={note && <Note text={note} />}>
      {items.length && <List items={items} />}
    </Panel>
  );
}
example.jsxNo problems
function Cart({ items }) {
  return (
    <div>
      {items.length > 0 && <List items={items} />}
      {!!items.length && <Total items={items} />}
      {Boolean(items.length) && <Coupon />}
      {items.length ? <Checkout /> : null}
    </div>
  );
}

Options#

OptionDefaultWhat it does
validStrategies["ternary", "coerce"]The allowed ways to write a condition. "ternary" is test ? <A /> : null. "coerce" is && with a boolean on the left.

With only "ternary", every && is reported:

example.jsxwith { "validStrategies": ["ternary"] }1 problem
const cart = <div>{!!items.length && <List items={items} />}</div>;
example.jsxwith { "validStrategies": ["ternary"] }No problems
const cart = <div>{items.length ? <List items={items} /> : null}</div>;

With only "coerce", a ternary is reported too, unless it ends in another element or in a value such as a string:

example.jsxwith { "validStrategies": ["coerce"] }1 problem
const cart = <div>{items.length ? <List items={items} /> : null}</div>;
example.jsxwith { "validStrategies": ["coerce"] }No problems
const cart = <div>{items.length > 0 && <List items={items} />}</div>;

const list = <div>{items.length ? <List items={items} /> : <Empty />}</div>;

const count = <div>{items.length ? items.length : "none"}</div>;

When not to use it#

The rule doesn't use types. So it also reports {user && <Profile user={user} />}, even when user is an object or undefined and nothing can leak.