When using conditional rendering with the && operator, React renders the number 0 instead of nothing when the left-hand expression evaluates to 0. This common issue occurs because React treats numbers as valid children and renders them, unlike boolean values which are skipped.
This issue occurs when developers use the logical AND (&&) operator for conditional rendering in JSX, expecting that a falsy value will prevent rendering entirely. However, React has specific rules about what it renders as children: it skips boolean values (true/false), null, and undefined, but it renders numbers—including 0—as text content. The most common scenario is using array.length or count variables directly with &&. For example, `items.length && <ItemList />` will display "0" when the array is empty, rather than rendering nothing. This happens because JavaScript's && operator returns the left operand if it's falsy, and 0 is both falsy in JavaScript logic but a valid renderable value in React. Understanding this distinction is crucial: while 0 is considered falsy in JavaScript conditionals, React treats it as a valid child element that should be rendered to the DOM. This behavior is intentional and consistent with React's rendering model, where numeric values are legitimate content that users might want to display.
The most reliable fix is to ensure your condition evaluates to a boolean rather than a number. Use comparison operators to make your intent explicit:
// ❌ Problem - renders 0 when items array is empty
{items.length && <ItemList items={items} />}
// ✅ Solution - explicit boolean comparison
{items.length > 0 && <ItemList items={items} />}
// ❌ Problem - renders 0 when count is zero
{messageCount && <MessageBadge count={messageCount} />}
// ✅ Solution - explicit check
{messageCount > 0 && <MessageBadge count={messageCount} />}This approach makes your code more readable and prevents the 0 from being rendered.
The double negation operator converts any value to its boolean equivalent. This is a concise way to ensure falsy values become false:
// ❌ Problem
{items.length && <ItemList items={items} />}
// ✅ Solution - double negation
{!!items.length && <ItemList items={items} />}
// ❌ Problem
{productsInStock && <ProductGrid />}
// ✅ Solution
{!!productsInStock && <ProductGrid />}The first ! converts the value to boolean and inverts it, the second ! inverts it back to the correct boolean value. While concise, explicit comparisons (step 1) are often more readable.
For maximum clarity, you can use the Boolean() constructor to convert values to boolean:
// ❌ Problem
{items.length && <ItemList items={items} />}
// ✅ Solution - Boolean constructor
{Boolean(items.length) && <ItemList items={items} />}
// Example with multiple conditions
{Boolean(user && user.isActive) && <ActiveUserBadge />}This is more verbose but makes your intention crystal clear, which can be valuable in team environments or complex conditionals.
The ternary operator gives you complete control over both branches and makes it obvious what renders in each case:
// ❌ Problem
{items.length && <ItemList items={items} />}
// ✅ Solution - ternary with null
{items.length > 0 ? <ItemList items={items} /> : null}
// ✅ Alternative - render empty state
{items.length > 0 ? (
<ItemList items={items} />
) : (
<EmptyState message="No items found" />
)}This approach is ideal when you want to render different content for true and false cases, not just conditionally show/hide.
When working with arrays, combine type checking with length validation for robust conditionals:
// ❌ Problem - might render 0
{data.items.length && <DataGrid data={data.items} />}
// ✅ Solution - type-safe check
{Array.isArray(data.items) && data.items.length > 0 && (
<DataGrid data={data.items} />
)}
// ✅ Alternative - helper function
const hasItems = (arr: unknown): arr is any[] => {
return Array.isArray(arr) && arr.length > 0;
};
{hasItems(data.items) && <DataGrid data={data.items} />}This prevents issues when data might be null, undefined, or not an array.
When working with nested data structures that might be undefined, combine optional chaining with boolean conversion:
// ❌ Problem - could render 0 or cause errors
{user.notifications.length && <NotificationList />}
// ✅ Solution - optional chaining + boolean
{(user?.notifications?.length ?? 0) > 0 && <NotificationList />}
// ✅ Alternative - explicit check
{Boolean(user?.notifications?.length) && <NotificationList />}The nullish coalescing operator (??) provides a default value when the chain results in null or undefined.
Add ESLint rules to prevent this issue during development:
npm install --save-dev eslint-plugin-reactAdd to your .eslintrc configuration:
{
"plugins": ["react"],
"rules": {
"react/jsx-no-leaked-render": ["warn", {
"validStrategies": ["ternary", "coerce"]
}]
}
}This rule warns when you use patterns that might render 0 unintentionally, helping catch the issue before it reaches production.
React's Rendering Behavior:
React's decision to render numeric children, including 0, is intentional and aligns with its design philosophy. The framework distinguishes between "nothing" (boolean false, null, undefined) and "something" (strings, numbers). This allows developers to intentionally render numeric values like counts and IDs without conversion.
Performance Considerations:
Different approaches have negligible performance differences. However, explicit boolean comparisons (>) are generally fastest as they avoid function calls. The !! operator and Boolean() constructor add a tiny overhead but are imperceptible in real applications. Choose based on readability and team conventions rather than performance.
TypeScript Integration:
TypeScript can help prevent this issue with proper typing. When defining component props, use boolean types for conditional flags rather than numbers:
// ❌ Allows numeric values that might cause issues
interface Props {
itemCount: number;
}
// ✅ Use explicit boolean for conditionals
interface Props {
hasItems: boolean;
itemCount: number;
}Framework-Specific Behaviors:
This behavior is specific to React. Other frameworks like Vue, Svelte, and Angular treat 0 as falsy and don't render it. If you're migrating from these frameworks, be aware of this difference in rendering semantics.
Preventing Issues in Reusable Components:
When building component libraries, document conditional rendering behavior clearly. Consider accepting boolean props rather than numeric values for show/hide logic, making the API less error-prone:
// ❌ API that can cause issues
<ItemList items={items} /> // Component must handle empty case
// ✅ Clearer API with explicit control
<ItemList items={items} showWhenEmpty={false} />Server-Side Rendering (SSR) Considerations:
This issue affects both client and server-side rendering identically. However, hydration mismatches can occur if client-side data differs from server-rendered data. Ensure your conditional logic is consistent between server and client to avoid hydration warnings.
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Warning: You provided a `selected` prop to a form field without an `onChange` handler
You provided a 'selected' prop without an onChange handler
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency