mantinedev/mantine · warning
[@mantine/hooks/use-focus-trap] Failed to find focusable ele
Error message
[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node
What it means
A development-only console.warn from useFocusTrap: the trap was activated on a DOM node, but no focusable element was found inside it, so focus could not be assigned. The hook still returns the ref and attaches it; it just could not focus anything. Suppressed in production builds.
Source
Thrown at packages/@mantine/hooks/src/use-focus-trap/use-focus-trap.ts:23
export function useFocusTrap(active = true): React.RefCallback<HTMLElement | null> {
const ref = useRef<HTMLElement>(null);
const focusNode = (node: HTMLElement) => {
let focusElement: HTMLElement | null = node.querySelector('[data-autofocus]');
if (!focusElement) {
const children = Array.from<HTMLElement>(node.querySelectorAll(FOCUS_SELECTOR));
focusElement = children.find(tabbable) || children.find(focusable) || null;
if (!focusElement && focusable(node)) {
focusElement = node;
}
}
if (focusElement) {
focusElement.focus({ preventScroll: true });
} else if (process.env.NODE_ENV === 'development') {
// oxlint-disable-next-line no-console
console.warn(
'[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node',
node
);
}
};
const setRef = useCallback(
(node: HTMLElement | null) => {
if (!active) {
return;
}
if (node === null) {
ref.current = null;
return;
}
if (ref.current === node) {View on GitHub (pinned to 8a284e2c2c)
Solutions
- Ensure at least one focusable element exists in the trapped subtree: add a close button, or set tabIndex={-1} on a heading/container to make it programmatically focusable
- If content loads async, activate the trap only after content is ready (conditional active prop) or focus a specific element manually
- Check for disabled/hidden focusables at activation time (buttons with disabled, elements under display:none are not focusable)
- Verify the ref is attached to the container that actually holds the interactive content
Example fix
// before
const focusTrapRef = useFocusTrap(true);
return <div ref={focusTrapRef}><p>No interactive elements here</p></div>;
// after
const focusTrapRef = useFocusTrap(true);
return (
<div ref={focusTrapRef}>
<h2 tabIndex={-1}>Dialog title</h2>
<button onClick={close}>Close</button>
</div>
); Defensive patterns
Strategy: validation
Validate before calling
const FOCUSABLE = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
function hasFocusableElement(node: HTMLElement | null): boolean {
return !!node && node.querySelectorAll(FOCUSABLE).length > 0;
} Type guard
const isFocusable = (el: Element): boolean =>
el instanceof HTMLElement &&
!el.hasAttribute('disabled') &&
(['BUTTON','INPUT','SELECT','TEXTAREA','A'].includes(el.tagName) || el.tabIndex >= 0) &&
el.offsetParent !== null; Try / catch
// Development-only warning, no throw; guard by checking content before activating: const ref = useFocusTrap(hasFocusableElement(containerRef.current));
Prevention
- Always include at least one focusable element (close button or tabIndex={-1} heading) inside trapped content
- Activate the trap only after async content has rendered
- Remember disabled or display:none elements are not focusable
- This warning appears only in development — reproduce locally with NODE_ENV=development
When it happens
Trigger: useFocusTrap(active = true) ref points to an element containing no focusable children (no button, input, a[href], tabindex element) and the node itself is not focusable. Fires from focusNode, called when the ref callback runs on activation.
Common situations: Modal/drawer/popover content that renders only non-interactive text; children rendered asynchronously (loading spinner) so focusables appear after the trap activates; all children disabled or hidden (aria-hidden, display:none); CSS hiding focusables at activation time; incorrect ref attached to a wrapper with purely presentational content.
Related errors
AI-assisted analysis of mantinedev/mantine@8a284e2c2c (2026-08-28).
Data as JSON: /api/errors/392ef16f2ae0419a.
Report an issue: GitHub.