tailwindlabs/headlessui · critical · Error

A <Transition /> is used but it is missing a `show={true | f

Error message

A <Transition /> is used but it is missing a `show={true | false}` prop.

What it means

<Transition> (TransitionRootFn) is a controlled visibility component: it needs to know whether content is shown. show may be inferred from a parent open-closed provider (e.g. inside a Dialog that uses CloseProvider), but if after that inference show is still undefined, Headless UI throws because it cannot decide the initial state. This is a required-prop guard, not a runtime state error.

Source

Thrown at packages/@headlessui-react/src/components/transition/transition.tsx:525

  let { show, appear = false, unmount = true, ...theirProps } = props as typeof props
  let internalTransitionRef = useRef<HTMLElement | null>(null)
  let requiresRef = shouldForwardRef(props)

  let transitionRef = useSyncRefs(
    ...(requiresRef ? [internalTransitionRef, ref] : ref === null ? [] : [ref])
  )

  // The TransitionChild will also call this hook, and we have to make sure that we are ready.
  useServerHandoffComplete()

  let usesOpenClosedState = useOpenClosed()

  if (show === undefined && usesOpenClosedState !== null) {
    show = (usesOpenClosedState & State.Open) === State.Open
  }

  if (show === undefined) {
    throw new Error('A <Transition /> is used but it is missing a `show={true | false}` prop.')
  }

  let [state, setState] = useState(show ? TreeStates.Visible : TreeStates.Hidden)

  let nestingBag = useNesting(() => {
    if (show) return
    setState(TreeStates.Hidden)
  })

  let [initial, setInitial] = useState(true)

  // Change the `initial` value
  let changes = useRef([show])
  useIsoMorphicEffect(() => {
    // We can skip this effect
    if (initial === false) {
      return
    }

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Add the show prop: <Transition show={isOpen} ...>
  2. Ensure the value passed to show is a boolean, default it: show={isOpen ?? false}
  3. If inside a Dialog, either rely on Dialog's open state (render Transition as a Dialog child) or pass show explicitly
  4. For always-on CSS transitions without visibility control, use plain CSS classes instead of Transition

Example fix

// before
<Transition enter="transition" enterFrom="opacity-0">
  <div>Content</div>
</Transition>

// after
<Transition show={isOpen} enter="transition" enterFrom="opacity-0">
  <div>Content</div>
</Transition>
Defensive patterns

Strategy: type-guard

Validate before calling

const show = maybeShow ?? false;
<Transition show={show} ...>...</Transition>

Type guard

const hasShowProp = (p) => typeof p.show === 'boolean';

Prevention

When it happens

Trigger: Rendering <Transition> or <Transition.Root> with no show prop and no surrounding open/closed context provider; passing show={undefined} due to conditional props or an unset variable; using <Transition appear> without show expecting it to behave like CSS-only animations.

Common situations: Copy-pasting a Transition example and deleting the show prop; wiring show to state that is undefined at first render (lazy-initialized stores); refactoring from Transition.Child to Transition without adding show; version differences in Dialog-provided open state inference.

Related errors


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