facebook/react · error · Error

299

299

Error message

Target container is not a DOM element.

What it means

createPortal validates its container with isValidContainer, which accepts only DOM nodes whose nodeType is ELEMENT (1), DOCUMENT (9), or DOCUMENT_FRAGMENT (11) (plus a special mount-point comment). Passing null/undefined — classically a failed document.getElementById — or a non-DOM value such as a selector string or a jQuery-wrapped object throws error 299 before any portal is created.

Source

Thrown at packages/react-dom/src/client/ReactDOMClientFB.js:82

    // $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype
    Set.prototype == null ||
    typeof Set.prototype.clear !== 'function' ||
    typeof Set.prototype.forEach !== 'function'
  ) {
    console.error(
      'React depends on Map and Set built-in types. Make sure that you load a ' +
        'polyfill in older browsers. https://react.dev/link/react-polyfills',
    );
  }
}

function createPortal(
  children: ReactNodeList,
  container: Element | DocumentFragment,
  key: ?string = null,
): React$Portal {
  if (!isValidContainer(container)) {
    throw new Error('Target container is not a DOM element.');
  }

  // TODO: pass ReactDOM portal implementation as third argument
  // $FlowFixMe[incompatible-type] The Flow type is opaque but there's no way to actually create it.
  return createPortalImpl(children, container, null, key);
}

// Overload the definition to the two valid signatures.
// Warning, this opts-out of checking the function body.
declare function flushSyncFromReconciler<R>(fn: () => R): R;
declare function flushSyncFromReconciler(): void;
function flushSyncFromReconciler<R>(fn: (() => R) | void): R | void {
  if (__DEV__) {
    if (isAlreadyRendering()) {
      console.error(
        'flushSync was called from inside a lifecycle method. React cannot ' +
          'flush when React is already rendering. Consider moving this call to ' +
          'a scheduler task or micro task.',

View on GitHub (pinned to eafeac097b)

Solutions

  1. Null-check the getElementById result before rendering the portal
  2. Ensure the container <div id="..."> exists in the DOM before the code runs (script at end of body, or defer)
  3. Fix mismatched or typo'd container ids between HTML and JavaScript
  4. Render the portal only after the container is mounted (e.g. inside useEffect)

Example fix

// before
createPortal(<Tooltip />, document.getElementById('tooltip-root'));

// after
const host = document.getElementById('tooltip-root');
if (host) {
  createPortal(<Tooltip />, host);
}
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidPortalContainer(node: unknown): boolean {
  return !!(
    node &&
    typeof node === 'object' &&
    (node.nodeType === 1 /* ELEMENT */ ||
      node.nodeType === 9 /* DOCUMENT */ ||
      node.nodeType === 11 /* DOCUMENT_FRAGMENT */)
  );
}

const host = document.getElementById('tooltip-root');
if (isValidPortalContainer(host)) {
  createPortal(<Tooltip />, host);
}

Type guard

function isValidPortalContainer(node: unknown): node is Element | Document | DocumentFragment {
  return !!(
    node &&
    typeof node === 'object' &&
    (node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11)
  );
}

Try / catch

try {
  createPortal(children, host);
} catch (e) {
  if (e instanceof Error && e.message === 'Target container is not a DOM element.') {
    throw new Error(`Portal host missing: ${host}`); // clearer context for debugging
  }
  throw e;
}

Prevention

When it happens

Trigger: createPortal(<Modal />, document.getElementById('modal-root')) when no element with that id exists; passing a CSS selector string, a jQuery/$() result, or a plain object instead of a DOM node.

Common situations: The portal container element is rendered later than the script runs; the id in the JSX/HTML does not match the id in JS; the container div was never added to the page template.

Related errors


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