tailwindlabs/headlessui · critical · Error
You provided an `onClose` prop to the `Dialog`, but the valu
Error message
You provided an `onClose` prop to the `Dialog`, but the value is not a function. Received: ${props.onClose} What it means
Headless UI's Dialog is a controlled component: it requires an onClose callback prop so it can inform you when the user requests the dialog to close (Escape key, backdrop click, focus-trap interactions). During render, DialogFn validates that props.onClose is actually a function and throws immediately if not. This is a fail-fast guard because a missing onClose leaves the dialog permanently stuck open.
Source
Thrown at packages/@headlessui-react/src/components/dialog/dialog.tsx:411
throw new Error(
`You provided an \`onClose\` prop to the \`Dialog\`, but forgot an \`open\` prop.`
)
}
if (!hasOnClose) {
throw new Error(
`You provided an \`open\` prop to the \`Dialog\`, but forgot an \`onClose\` prop.`
)
}
if (!usesOpenClosedState && typeof props.open !== 'boolean') {
throw new Error(
`You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${props.open}`
)
}
if (typeof props.onClose !== 'function') {
throw new Error(
`You provided an \`onClose\` prop to the \`Dialog\`, but the value is not a function. Received: ${props.onClose}`
)
}
if ((open !== undefined || transition) && !rest.static) {
return (
<MainTreeProvider>
<Transition show={open} transition={transition} unmount={rest.unmount}>
<InternalDialog ref={ref} {...rest} />
</Transition>
</MainTreeProvider>
)
}
return (
<MainTreeProvider>
<InternalDialog ref={ref} open={open} {...rest} />
</MainTreeProvider>View on GitHub (pinned to eea57cf46f)
Solutions
- Add (or fix) the onClose prop: <Dialog open={isOpen} onClose={() => setIsOpen(false)}>
- If conditionally rendering props, ensure onClose is always a function, e.g. onClose={handleClose ?? (() => {})}
- Check that the handler you pass actually exists (no typos like onclose or onCloseHandler undefined at runtime)
- In TypeScript, don't cast props to any when wrapping Dialog — keep the required onClose in your wrapper's own props
Example fix
// before
<Dialog open={isOpen}>
<Dialog.Panel>...</Dialog.Panel>
</Dialog>
// after
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
<Dialog.Panel>...</Dialog.Panel>
</Dialog> Defensive patterns
Strategy: type-guard
Validate before calling
const canRenderDialog = typeof onClose === 'function' && typeof open === 'boolean';
return canRenderDialog ? <Dialog open={open} onClose={onClose}>...</Dialog> : null; Type guard
const isDialogProps = (p) => typeof p.open === 'boolean' && typeof p.onClose === 'function';
Prevention
- Type your wrapper's props so onClose is required (no optional marker)
- Default onClose in wrappers: onClose = () => {}
- Add a dev-only assertion before rendering Dialog
When it happens
Trigger: Rendering <Dialog open={...}> without an onClose prop; passing onClose={null} or onClose={undefined} (e.g. conditionally spreading props); passing a value like onClose={dialogState.close} where dialogState is undefined so the value evaluates to undefined; passing a string or other non-function by typo.
Common situations: Converting an uncontrolled example to TypeScript and dropping the prop; copying Dialog from docs but forgetting the close handler; spreading {...rest} that overwrites onClose with undefined; version upgrades where prop types became stricter (onClose required).
Related errors
- You have to provide an `open` and an `onClose` prop to the `
- You provided an `onClose` prop to the `Dialog`, but forgot a
- You provided an `open` prop to the `Dialog`, but forgot an `
- A <Transition /> is used but it is missing a `show={true | f
- You forgot to provide an `open` prop to the `Dialog`.
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/00518b45599abee8.
Report an issue: GitHub.