Initializing Enclave...

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 false to a DOM attribute (e.g., width, height, tabIndex, span, size) that expects a string or number — React refuses to serialize false to 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 false with undefined to 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 elsewidth, 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:

  1. A parent passes a conditional valuewidth={isActive && someWidth} — and isActive is false, so the expression short-circuits to false instead of undefined.
  2. A default prop is never set — the component receives undefined from props, a developer adds || false as a fallback thinking it means "no value", not realizing false !== undefined for DOM serialization.
  3. API data coercion — a backend returns 0 or null, a developer normalizes it to false somewhere in a selector or mapper.

Blast radius:

  • Layout breakage — missing width/height on media elements causes reflow jank and CLS (Cumulative Layout Shift) regression in Core Web Vitals.
  • Accessibility failuresaria-* attributes receiving false as a string (aria-expanded="false" is valid; aria-level="false" is not) break screen reader announcements.
  • Table/grid corruptioncolSpan={false} or rowSpan={false} causes browsers to default to 1, silently collapsing merged cells.
  • Form validation bypassmaxLength={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.

Related Diagnostics

"Part of the Syntax Utility Matrix."

View all 153 Syntax Tools →