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

  1. 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
  2. If content loads async, activate the trap only after content is ready (conditional active prop) or focus a specific element manually
  3. Check for disabled/hidden focusables at activation time (buttons with disabled, elements under display:none are not focusable)
  4. 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

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.