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
- Add at least one focusable element inside the Dialog panel, such as a close <button> or the action buttons.
- Point the initialFocus prop/ref at a real, mounted element: <Dialog :initialFocus="myRef"> (or pass initialFocus to FocusTrap).
- If content is async, delay rendering the Dialog until content exists, or use `unmount={false}`/keep a persistent focusable fallback element.
- 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
- Always render at least one focusable action (e.g. Close button) inside the DialogPanel.
- Pass :initial-focus to a ref on an element that is always mounted and enabled.
- Delay opening the dialog until async content is rendered.
- Run a keyboard-only smoke test (Tab) on every dialog you build.
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
- There are no focusable elements inside the <FocusTrap />
- Invalid role [${role}] passed to <Dialog />. Only `dialog` a
- You forgot to provide an `open` prop to the `Dialog`.
- You provided an `open` prop to the `Dialog`, but the value i
- A <DialogBackdrop /> component is being used, but a <DialogP
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/96000da76f489930.
Report an issue: GitHub.