tailwindlabs/headlessui · warning

There are no focusable elements inside the <FocusTrap />

Error message

There are no focusable elements inside the <FocusTrap />

What it means

This warning is emitted by Headless UI React's <FocusTrap /> (used internally by <Dialog>) during initial focus when it cannot find any focusable element inside the trap container. The useInitialFocus hook tries the `initialFocus` prop/ref, then the container's first focusable element; if every strategy fails it warns and the dialog opens with nothing focused, which breaks keyboard accessibility.

Source

Thrown at packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx:402

            return // Worked, bail
          }
        }

        // Try to focus the first focusable element.
        else if (focusIn(containerElement!, Focus.First) !== FocusResult.Error) {
          return // Worked, bail
        }

        // Try the fallback
        if (initialFocusFallback?.current) {
          focusElement(initialFocusFallback.current)
          if (ownerDocument?.activeElement === initialFocusFallback.current) {
            return // Worked, bail
          }
        }

        // Nothing worked
        console.warn('There are no focusable elements inside the <FocusTrap />')
      }

      previousActiveElement.current = ownerDocument?.activeElement as HTMLElement
    })
  }, [initialFocusFallback, enabled, features])

  return previousActiveElement
}

function useFocusLock(
  features: FocusTrapFeatures,
  {
    ownerDocument,
    container,
    containers,
    previousActiveElement,
  }: {
    ownerDocument: Document | null

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Add at least one focusable element inside DialogPanel, e.g. action buttons or a close <button>.
  2. Pass initialFocus pointing to a mounted element: <Dialog initialFocus={cancelRef}> (or initialFocus on FocusTrap).
  3. Ensure the ref target is not null: use a callback/useRef assigned to an element that always renders, and check it isn't disabled or hidden.
  4. If content is async, render the Dialog only when content is ready, or keep a persistent focusable fallback (e.g. tabindex={-1} on the panel wrapper and pass it as initialFocus).

Example fix

// before
<Dialog onClose={close}>
  <DialogPanel>
    <p>Just text, nothing focusable</p>
  </DialogPanel>
</Dialog>

// after
function Example() {
  const cancelRef = useRef(null)
  return (
    <Dialog onClose={close} initialFocus={cancelRef}>
      <DialogPanel>
        <p>Are you sure?</p>
        <button ref={cancelRef} onClick={close}>Cancel</button>
        <button onClick={confirm}>Confirm</button>
      </DialogPanel>
    </Dialog>
  )
}
Defensive patterns

Strategy: validation

Validate before calling

// React: check before opening / at render
function hasFocusable(root: HTMLElement | null): boolean {
  if (!root) return false
  return root.querySelector(
    'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])'
  ) !== null
}
// in the component
const panelRef = useRef<HTMLDivElement>(null)
const open = () => {
  if (!cancelRef.current && !hasFocusable(panelRef.current)) {
    panelRef.current?.setAttribute('tabindex', '-1') // fallback focus target
  }
  setOpen(true)
}

Type guard

function isFocusable(el: Element | null): el is HTMLElement {
  if (!el) return false
  return (
    !el.hasAttribute('disabled') &&
    el.getAttribute('aria-hidden') !== 'true' &&
    (el.matches('a[href],button,input,select,textarea,[tabindex]') ||
      el.getAttribute('contenteditable') === 'true')
  )
}

Prevention

When it happens

Trigger: Rendering a <Dialog> whose panel contains no focusable elements (no buttons, inputs, links, or [tabindex] elements) while initialFocus is unset, or passing an initialFocus ref whose current target is null/detached/disabled at mount time. Content that renders empty on first paint (async data) also triggers it.

Common situations: Dialogs with only text and a close button rendered conditionally after data loads; initialFocus refs attached to elements inside a v-if/false branch or removed by state changes; elements with `disabled`, `hidden`, `aria-hidden`, or `display:none`; SSR timing issues where the ref isn't populated when the trap activates.

Related errors


AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28). Data as JSON: /api/errors/b3a5d6006c07d4eb. Report an issue: GitHub.