There is no v-if and no {{#if}} in JSX, because JavaScript already has conditionals. Four tools, in rough order of preference: return null early when the component should render nothing; assign markup to a variable with a normal if before the return; use a ternary inside the braces; or use && when there is no "else" branch. React 7,897 treats null, undefined, false and true as children that render nothing, which is what makes all four work.
The && form has one famous trap. expr && <JSX/> evaluates to the left operand when that operand is falsy, and React renders a number, so items.length && <List/> prints a bare 0 on an empty list. Compare the count instead: items.length > 0 && ....
<div id="root" style="font:15px system-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script type="text/babel" data-type="module">
import React from 'https://esm.sh/react@19.3.0';
import { createRoot } from 'https://esm.sh/react-dom@19.3.0/client';
function Row({ label, items }) {
if (!items) return null; // 1. early return
let tag; // 2. if before the return
if (items.length > 3) tag = <b> (many)</b>;
return ( // 3. ternary 4. &&
<li>{label}: {items.length ? items.join(', ') : <i>empty</i>}{tag}
<br />broken: {items.length && ' in stock'}
<br />fixed: {items.length > 0 && ' in stock'}</li>
);
}
const lists = { Keyboards: ['K1', 'K2', 'K3', 'K4'], Monitors: [], Cables: null };
createRoot(document.getElementById('root')).render(
<ul>{Object.entries(lists).map(([k, v]) => <Row key={k} label={k} items={v} />)}</ul>
);
</script>
Cables disappears entirely: its component returned null. Monitors shows the bug — a literal 0 where nothing should appear — while the "fixed" line stays blank. Keep conditions shallow: nested ternaries inside JSX become unreadable past the second level, and the cure is to pull the branch into its own component.