tailwindlabs/headlessui · error · Error

You provided an `open` prop to the `Dialog`, but forgot an `

Error message

You provided an `open` prop to the `Dialog`, but forgot an `onClose` prop.

What it means

The inverse validation: `open` was provided but `onClose` was not (and no OpenClosed context). Dialog needs a callback to signal close requests (Escape key, backdrop click, scroll-lock teardown); without it the dialog would be impossible to dismiss, so it throws.

Source

Thrown at packages/@headlessui-react/src/components/dialog/dialog.tsx:399

  // Validations
  let usesOpenClosedState = useOpenClosed()
  let hasOpen = props.hasOwnProperty('open') || usesOpenClosedState !== null
  let hasOnClose = props.hasOwnProperty('onClose')

  if (!hasOpen && !hasOnClose) {
    throw new Error(
      `You have to provide an \`open\` and an \`onClose\` prop to the \`Dialog\` component.`
    )
  }

  if (!hasOpen) {
    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 (

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Add `<Dialog open={open} onClose={() => setOpen(false)}>`.
  2. If you truly want it non-dismissible, still pass a no-op: `onClose={() => {}}` and disable outside-click/escape via `static` + custom handling.
  3. Verify spreads: `<Dialog open={open} {...rest}>` where rest actually contains onClose.

Example fix

// before
<Dialog open={isOpen}>...</Dialog>

// after
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>...</Dialog>
Defensive patterns

Strategy: type-guard

Validate before calling

const close = useCallback(() => setOpen(false), [])
// <Dialog open={open} onClose={close}>

Type guard

const hasOnClose = (p: { onClose?: unknown }): boolean => typeof p.onClose === 'function'

Prevention

When it happens

Trigger: `<Dialog open={isOpen}>` with no `onClose`; passing onClose via a spread that evaluates undefined; renaming the handler (e.g., `onCloseRequest`) so the real prop is missing.

Common situations: Initial scaffolding where only `open` was wired; prop drilling typos; using `onRequestClose` (react-native-modal naming) instead of `onClose`.

Related errors


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