facebook/react · error · Error

${tag} is a self-closing tag and must neither have `children

Error message

${tag} is a self-closing tag and must neither have `children` nor use `dangerouslySetInnerHTML`.

What it means

Thrown by the generic pushSelfClosing helper (message interpolates the actual tag name) when one of the void elements it serves — base, area, br, col, embed, hr, keygen, param, source, track, wbr (this helper also serializes meta and img through their own paths) — receives a non-null children or dangerouslySetInnerHTML prop. These tags have no end tag in HTML, so any children would be parsed as siblings and corrupt the document; React aborts instead.

Source

Thrown at packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js:3547

function pushSelfClosing(
  target: Array<Chunk | PrecomputedChunk>,
  props: Object,
  tag: string,
  formatContext: FormatContext,
): null {
  target.push(startChunkForTag(tag));

  for (const propKey in props) {
    if (hasOwnProperty.call(props, propKey)) {
      const propValue = props[propKey];
      if (propValue == null) {
        continue;
      }
      switch (propKey) {
        case 'children':
        case 'dangerouslySetInnerHTML':
          throw new Error(
            `${tag} is a self-closing tag and must neither have \`children\` nor ` +
              'use `dangerouslySetInnerHTML`.',
          );
        default:
          pushAttribute(target, propKey, propValue);
          break;
      }
    }
  }

  pushViewTransitionAttributes(target, formatContext);

  target.push(endOfStartTagSelfClosing);
  return null;
}

function pushStartMenuItem(
  target: Array<Chunk | PrecomputedChunk>,

View on GitHub (pinned to eafeac097b)

Solutions

  1. Self-close the element and remove its children entirely
  2. Strip children before spreading props onto void elements: const {children, ...rest} = props
  3. If content was intended, move it to a sibling element instead of inside the void tag

Example fix

// before
<br></br>
<hr {...props} /> {/* props.children present */}

// after
<br />
const {children, ...rest} = props;
<hr {...rest} />
Defensive patterns

Strategy: type-guard

Validate before calling

const VOID = new Set(['base','area','br','col','embed','hr','keygen','param','source','track','wbr','meta','link','img','input']);
function safeTagProps(tag: string, props: Record<string, unknown>) {
  if (VOID.has(tag)) {
    const {children, dangerouslySetInnerHTML, ...rest} = props as any;
    return rest;
  }
  return props;
}

Type guard

type VoidElementProps = {children?: never; dangerouslySetInnerHTML?: never};

Prevention

When it happens

Trigger: JSX like <br>spacing</br>, <hr {...props} /> where props contains children, <source src={v}>{label}</source>, <col>{header}</col>, or any void element receiving children via prop spread.

Common situations: Generic HTML-emitting components that spread user props onto arbitrary tags; HTML-to-JSX converters producing closed void tags with content; copy-paste from non-strict templates; children leaking through {...rest} in table/part components (col, source in picture, track in video).

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/3535a0277c3aab52. Report an issue: GitHub.