tailwindlabs/headlessui · error · Error
A <DialogBackdrop /> component is being used, but a <DialogP
Error message
A <DialogBackdrop /> component is being used, but a <DialogPanel /> component is missing.
What it means
When @headlessui-vue's DialogBackdrop mounts, it expects a sibling DialogPanel to exist inside the same Dialog (it wires backdrop click handling and aria wiring against api.panelRef). If panelRef is still null onMounted, the required <DialogPanel /> is missing and the component throws because the backdrop cannot function correctly without it.
Source
Thrown at packages/@headlessui-vue/src/components/dialog/dialog.ts:405
// ---
export let DialogBackdrop = defineComponent({
name: 'DialogBackdrop',
props: {
as: { type: [Object, String], default: 'div' },
id: { type: String, default: () => `headlessui-dialog-backdrop-${useId()}` },
},
inheritAttrs: false,
setup(props, { attrs, slots, expose }) {
let api = useDialogContext('DialogBackdrop')
let internalBackdropRef = ref(null)
expose({ el: internalBackdropRef, $el: internalBackdropRef })
onMounted(() => {
if (api.panelRef.value === null) {
throw new Error(
`A <DialogBackdrop /> component is being used, but a <DialogPanel /> component is missing.`
)
}
})
return () => {
let { id, ...theirProps } = props
let ourProps = {
id,
ref: internalBackdropRef,
'aria-hidden': true,
}
return h(ForcePortalRoot, { force: true }, () =>
h(Portal, () =>
render({
ourProps,
theirProps: { ...attrs, ...theirProps },View on GitHub (pinned to eea57cf46f)
Solutions
- Add a <DialogPanel> inside the same <Dialog>, containing the dialog's content.
- If you replaced DialogPanel with a plain div, switch it back to DialogPanel and style it via its props/class.
- Ensure DialogPanel is not wrapped in v-if="false" or lazy-rendered so it exists when the backdrop mounts.
Example fix
// before <Dialog :open="open"> <DialogBackdrop class="bg-black/50" /> <div class="...">...</div> </Dialog> // after <Dialog :open="open"> <DialogBackdrop class="bg-black/50" /> <DialogPanel class="...">...</DialogPanel> </Dialog>
Defensive patterns
Strategy: validation
Validate before calling
// Only render the backdrop when the panel is present in the same Dialog <Dialog :open="open"> <DialogBackdrop class="bg-black/50" /> <DialogPanel v-if="hasContent"><slot /></DialogPanel> </Dialog>
Prevention
- Treat DialogBackdrop/DialogTitle/DialogPanel as a set: never render the backdrop without the panel.
- Use a linter/editor snippet that scaffolds the full dialog trio together.
- Avoid conditionally removing DialogPanel while the backdrop is mounted.
When it happens
Trigger: Rendering <DialogBackdrop> inside <Dialog> but omitting <DialogPanel>; replacing DialogPanel with a plain <div>; ordering/mounting issues where the panel component is conditionally not rendered.
Common situations: Refactoring dialog markup and dropping the DialogPanel wrapper; using DialogBackdrop for styling but keeping content in raw divs; upgrading from patterns where panels were implicit.
Related errors
- You forgot to provide an `open` prop to the `Dialog`.
- You provided an `open` prop to the `Dialog`, but the value i
- Invalid role [${role}] passed to <Dialog />. Only `dialog` a
- There are no focusable elements inside the <FocusTrap />
- Missing parent
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/3624c89758dd0e98.
Report an issue: GitHub.