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 Vue'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` ref first, then falls back to focusing the first focusable element via focusIn(container, Focus.First | Focus.NoScroll); when that returns FocusResult.Error the warning fires and the dialog opens with no element focused.

Source

Thrown at packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts:331

          let activeElement = ownerDocument.value?.activeElement as HTMLElement

          if (initialFocusElement) {
            if (initialFocusElement === activeElement) {
              previousActiveElement.value = activeElement
              return // Initial focus ref is already the active element
            }
          } else if (containerElement!.contains(activeElement)) {
            previousActiveElement.value = activeElement
            return // Already focused within Dialog
          }

          // Try to focus the initialFocus ref
          if (initialFocusElement) {
            focusElement(initialFocusElement)
          } else {
            if (focusIn(containerElement!, Focus.First | Focus.NoScroll) === FocusResult.Error) {
              console.warn('There are no focusable elements inside the <FocusTrap />')
            }
          }

          previousActiveElement.value = ownerDocument.value?.activeElement as HTMLElement
        })
      },
      { immediate: true, flush: 'post' }
    )
  })

  return previousActiveElement
}

function useFocusLock(
  {
    ownerDocument,
    container,
    containers,

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Add at least one focusable element inside the Dialog panel, such as a close <button> or the action buttons.
  2. Point the initialFocus prop/ref at a real, mounted element: <Dialog :initialFocus="myRef"> (or pass initialFocus to FocusTrap).
  3. If content is async, delay rendering the Dialog until content exists, or use `unmount={false}`/keep a persistent focusable fallback element.
  4. Ensure the intended target isn't disabled, hidden, or negatively tabbable (tabindex="-1" alone is fine for programmatic focus, but display:none elements are not focusable).

Example fix

// before
<Dialog @close="close">
  <DialogPanel>
    <p>Just text, nothing focusable</p>
  </DialogPanel>
</Dialog>

// after
<Dialog @close="close">
  <DialogPanel>
    <p>Just text</p>
    <button @click="close">Close</button>
  </DialogPanel>
</Dialog>

<!-- or target a specific element -->
<Dialog @close="close" :initialFocus="cancelButtonRef">
  <DialogPanel>
    <button ref="cancelButtonRef" @click="close">Cancel</button>
  </DialogPanel>
</Dialog>
Defensive patterns

Strategy: validation

Validate before calling

// Vue: verify focusable content exists before opening
import { focusElement } from '@headlessui/vue'
function hasFocusable(el: HTMLElement | null): boolean {
  if (!el) return false
  return el.querySelector(
    'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])'
  ) !== null
}
function openDialog(panel: HTMLElement | null, initialFocusEl?: HTMLElement) {
  if (!initialFocusEl && !hasFocusable(panel)) {
    console.warn('Dialog has no focusable content; adding fallback focus target')
    panel?.setAttribute('tabindex', '-1')
  }
  isOpen.value = 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> (or <FocusTrap>) whose content contains no focusable elements (no buttons, links, inputs, [tabindex] elements), while the initialFocus ref is unset or points to a detached/hidden element. Also occurs when the content is conditionally rendered empty on first paint, or when all children are `hidden`/`disabled`/`inert`.

Common situations: Dialogs that only display text plus a close button that renders conditionally; content loaded async so the panel is empty when the trap activates; custom initialFocus ref attached to an element removed from the DOM (e.g. v-if becomes false); using `static` or `unmount` props so the panel mounts before its children do.

Related errors


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