mantinedev/mantine · warning

[@mantine/hooks/use-focus-trap] Ref node is not part of the

Error message

[@mantine/hooks/use-focus-trap] Ref node is not part of the dom

What it means

A development-only console.warn from useFocusTrap: the element assigned to the trap ref has no root node (node.getRootNode() is falsy), meaning it is detached from the DOM. The trap skips focusing and warns. It runs inside a setTimeout to let React attach nodes, so it fires a frame after activation.

Source

Thrown at packages/@mantine/hooks/src/use-focus-trap/use-focus-trap.ts:51

        return;
      }

      if (node === null) {
        ref.current = null;
        return;
      }

      if (ref.current === node) {
        return;
      }

      // Delay processing the HTML node by a frame. This ensures focus is assigned correctly.
      setTimeout(() => {
        if (node.getRootNode()) {
          focusNode(node);
        } else if (process.env.NODE_ENV === 'development') {
          // oxlint-disable-next-line no-console
          console.warn('[@mantine/hooks/use-focus-trap] Ref node is not part of the dom', node);
        }
      });

      ref.current = node;
    },
    [active]
  );

  useEffect(() => {
    if (!active) {
      return undefined;
    }

    if (ref.current) {
      setTimeout(() => {
        if (ref.current) {
          focusNode(ref.current);
        }

View on GitHub (pinned to 8a284e2c2c)

Solutions

  1. Check for mount/unmount races: ensure the component holding the trap stays mounted while active is true
  2. If using StrictMode, note the double-invoke in development can trigger this transiently — verify it does not occur in production behavior
  3. Ensure portal containers are attached to document.body before rendering trapped content
  4. Delay activation (active prop) until the container element is confirmed in the DOM

Example fix

// before
const focusTrapRef = useFocusTrap(true);
// component unmounts/remounts rapidly -> detached node
return mounted && <div ref={focusTrapRef}>...</div>;

// after
const focusTrapRef = useFocusTrap(mounted && contentReady);
return mounted ? <div ref={focusTrapRef}>...</div> : null;
Defensive patterns

Strategy: validation

Validate before calling

function isNodeInDocument(node: HTMLElement | null): boolean {
  return !!node && node.isConnected && !!node.getRootNode();
}

// before activating the trap
const active = isNodeInDocument(containerRef.current);

Type guard

const isAttachedNode = (node: HTMLElement | null): node is HTMLElement =>
  node !== null && node.isConnected;

Try / catch

// No throw occurs; the hook warns and skips focus. Guard lifecycle instead:
useEffect(() => {
  if (containerRef.current?.isConnected) setActive(true);
}, []);

Prevention

When it happens

Trigger: The ref callback receives a node that is not connected to the document — element created but never mounted, unmounted before the timeout runs, or rendered into a detached container. Happens when the component using useFocusTrap unmounts immediately, or when the ref node lives in a fragment/container removed within the same tick.

Common situations: Modal/drawer component that mounts and immediately unmounts (animation libraries removing nodes, conditional rendering flicker); refs on elements inside portals created into detached containers; StrictMode double-mount/unmount cycles in development; race conditions where active toggles true then false quickly.

Related errors


AI-assisted analysis of mantinedev/mantine@8a284e2c2c (2026-08-28). Data as JSON: /api/errors/b69c693464569712. Report an issue: GitHub.