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
- Check for mount/unmount races: ensure the component holding the trap stays mounted while active is true
- If using StrictMode, note the double-invoke in development can trigger this transiently — verify it does not occur in production behavior
- Ensure portal containers are attached to document.body before rendering trapped content
- 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
- Ensure the component holding the trap ref stays mounted while the trap is active
- Attach portal containers to document.body before rendering trapped content
- Beware rapid mount/unmount from animation libraries and conditional rendering
- Expect transient occurrences under React StrictMode in development; verify production behavior
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.