How to Fix React Warning: Received `false` for a Non-Boolean DOM Attribute
Bug Severity: HIGH | UX/User Impact: MODERATE | Time to Fix: 5 mins
TL;DR
- What broke: A React component is passing the boolean
falseto a DOM attribute (e.g.,width,height,tabIndex,span,size) that expects a string or number — React refuses to serializefalseto the DOM for non-boolean attributes and throws a console warning; in some cases the attribute renders as the literal string"false"or is dropped entirely. - How to fix it: Replace
falsewithundefinedto omit the attribute entirely, or coerce it to a valid string/number value using a ternary or logical expression. - Action: Use our Client-Side Sandbox above to auto-refactor this — paste your component and it will identify every offending prop and emit corrected code instantly.
The Incident (What Does the Error Mean?)
Raw browser console output:
Warning: Received `false` for a non-boolean attribute `width`.
If you want to write it to the DOM, pass a string instead:
width="false" or width={value.toString()}
at img
at MyComponent
React's DOM reconciler validates every prop before flushing to the real DOM. For boolean HTML attributes (disabled, checked, readOnly), false is a valid signal to omit the attribute. For everything else — width, height, tabIndex, colSpan, rowSpan, size, maxLength, data-* custom attributes, etc. — false is not a valid DOM value. React either drops it or, in older versions, serializes the literal string "false" into the markup, which breaks layout, accessibility, and downstream attribute-dependent logic.
Immediate consequence: The attribute is either missing or corrupted in the rendered HTML. An <img width={false}> loses its width constraint. A <td colSpan={false}> breaks table layout. An <input maxLength={false}> removes character limits silently.
The Attack Vector / Blast Radius
This is a data-flow bug, not a typo. It surfaces when:
- A parent passes a conditional value —
width={isActive && someWidth}— andisActiveisfalse, so the expression short-circuits tofalseinstead ofundefined. - A default prop is never set — the component receives
undefinedfrom props, a developer adds|| falseas a fallback thinking it means "no value", not realizingfalse !== undefinedfor DOM serialization. - API data coercion — a backend returns
0ornull, a developer normalizes it tofalsesomewhere in a selector or mapper.
Blast radius:
- Layout breakage — missing
width/heighton media elements causes reflow jank and CLS (Cumulative Layout Shift) regression in Core Web Vitals. - Accessibility failures —
aria-*attributes receivingfalseas a string (aria-expanded="false"is valid;aria-level="false"is not) break screen reader announcements. - Table/grid corruption —
colSpan={false}orrowSpan={false}causes browsers to default to1, silently collapsing merged cells. - Form validation bypass —
maxLength={false}drops the character limit entirely.
In SSR (Next.js, Remix), this also generates a hydration mismatch — the server renders one DOM shape, the client reconciles a different one, causing a full subtree remount.
How to Fix It (The Solution)
Basic Fix — Use undefined to Omit the Attribute
The correct signal to React for "do not render this attribute" is undefined, not false.
- <img src={src} width={isLoaded && imageWidth} />
+ <img src={src} width={isLoaded ? imageWidth : undefined} />
- <input maxLength={hasLimit && 255} />
+ <input maxLength={hasLimit ? 255 : undefined} />
Enterprise Best Practice — Prop Normalization at the Component Boundary
Never trust that upstream callers pass valid DOM-compatible values. Normalize at the component's prop destructuring layer and use TypeScript to enforce it at compile time.
- function MediaCard({ width, height, colSpan }) {
- return (
- <td colSpan={colSpan}>
- <img width={width} height={height} />
- </td>
- );
- }
+ interface MediaCardProps {
+ width?: number; // undefined = omit attribute
+ height?: number;
+ colSpan?: number;
+ }
+
+ function MediaCard({ width, height, colSpan }: MediaCardProps) {
+ // Normalize: coerce falsy non-zero values to undefined
+ const safeWidth = width || undefined;
+ const safeHeight = height || undefined;
+ const safeColSpan = colSpan || undefined;
+
+ return (
+ <td colSpan={safeColSpan}>
+ <img width={safeWidth} height={safeHeight} />
+ </td>
+ );
+ }
For data-* and aria-* attributes that legitimately need the string "false":
- <div aria-expanded={isOpen && true} />
+ <div aria-expanded={isOpen ? 'true' : 'false'} />
Note: aria-expanded accepts the string "false", not the boolean. React handles aria-* specially — but explicit string coercion is safer and more readable.
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your component trees, prop names, and internal data schemas. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing component into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. ESLint — eslint-plugin-react rule react/no-danger is not enough. Enable these:
// .eslintrc.json
{
"rules": {
"react/jsx-no-useless-fragment": "warn",
"no-restricted-syntax": [
"error",
{
"selector": "JSXAttribute[value.type='Literal'][value.value=false]",
"message": "Do not pass boolean false to a JSX attribute. Use undefined to omit or a string for explicit values."
}
]
}
}
2. TypeScript strict mode — set \"strict\": true in tsconfig.json. Typed component interfaces with number | undefined (not number | boolean | false) will catch this at compile time before it reaches the browser.
3. Storybook + Chromatic visual regression — any false-induced layout collapse (missing width/height) will show up as a visual diff in CI before merge.
4. React Testing Library assertion:
// In your component test
it('does not render width attribute when inactive', () => {
render(<MediaCard isActive={false} imageWidth={400} />);
const img = screen.getByRole('img');
// Should be absent, not "false"
expect(img).not.toHaveAttribute('width');
});
5. Pre-commit hook with react-dom/server snapshot test — render the component to static HTML with renderToStaticMarkup and assert no attribute contains the literal string "false" where a numeric or omitted value is expected. Catches SSR hydration mismatches before they hit production.