tailwindlabs/headlessui · error · Error

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

Error message

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

What it means

The Dialog render-time validation found an `onClose` prop but no `open` prop and no OpenClosed context state. Without `open`, the Dialog has no source of truth for visibility, so it refuses to render rather than silently never closing/opening.

Source

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

function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
  props: DialogProps<TTag>,
  ref: Ref<HTMLElement>
) {
  let { transition = false, open, ...rest } = props

  // 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(

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Add the boolean: `<Dialog open={isOpen} onClose={handleClose}>`.
  2. Check conditional spreads to ensure `open` is always included when `onClose` is.
  3. Remove `defaultOpen`; Dialog is controlled-only.

Example fix

// before
<Dialog onClose={() => setOpen(false)}>...</Dialog>

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

Strategy: type-guard

Validate before calling

// Always pass open alongside onClose
// <Dialog open={open ?? false} onClose={close}>

Type guard

const hasOpenProp = (p: object): boolean => 'open' in p || 'defaultOpen' in p && false /* unsupported */

Prevention

When it happens

Trigger: `<Dialog onClose={handleClose}>` without `open`; conditionally spreading props where the `open` key is dropped (`{...(cond ? {open} : {})}`); passing `defaultOpen` (which Dialog doesn't support) instead of `open`.

Common situations: Thinking `defaultOpen` or the presence of onClose implies controlled state; prop spreading bugs with conditional objects; migrating from Radix/native dialog APIs.

Related errors


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