tailwindlabs/headlessui · error · Error

A <Transition.Child /> is used but it is missing a parent <T

Error message

A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.

What it means

<Transition.Child> must be rendered inside a <Transition> or <Transition.Root> because it reads shared transition state (show/appear/initial) from React context. useTransitionContext consumes that context and throws when it is null, meaning no parent Transition provided it. This guards against children that would otherwise animate incorrectly or not at all.

Source

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

  afterLeave?: () => void
}

type TransitionChildPropsWeControl = never

export type TransitionChildProps<TTag extends ReactTag> = Props<
  TTag,
  TransitionChildRenderPropArg,
  TransitionChildPropsWeControl,
  PropsForFeatures<typeof TransitionChildRenderFeatures> &
    TransitionClasses &
    TransitionEvents & { transition?: boolean; appear?: boolean }
>

function useTransitionContext() {
  let context = useContext(TransitionContext)

  if (context === null) {
    throw new Error(
      'A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.'
    )
  }

  return context
}

function useParentNesting() {
  let context = useContext(NestingContext)

  if (context === null) {
    throw new Error(
      'A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.'
    )
  }

  return context
}

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Wrap the Transition.Child elements in <Transition.Root show={isOpen}> (or <Transition show={isOpen}>)
  2. If you don't need a transition, replace Transition.Child with a plain element or Dialog backdrop/panel directly
  3. If you meant a standalone transition, use <Transition> itself instead of <Transition.Child>
  4. Check that you didn't accidentally render the children through a portal or extracted component outside the Transition tree

Example fix

// before
<Transition.Child
  as={Fragment}
  enter="ease-out duration-300"
  enterFrom="opacity-0"
>
  <div className="fixed inset-0 bg-black" />
</Transition.Child>

// after
<Transition.Root show={isOpen} as={Fragment}>
  <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0">
    <div className="fixed inset-0 bg-black" />
  </Transition.Child>
</Transition.Root>
Defensive patterns

Strategy: validation

Validate before calling

import { Transition } from '@headlessui/react';
// Always structure JSX so Transition.Child is lexically inside Transition.Root
const tree = (
  <Transition.Root show={open}>
    <Transition.Child as={Fragment}>...</Transition.Child>
  </Transition.Root>
);

Prevention

When it happens

Trigger: Rendering <Transition.Child> as a sibling or outside of <Transition.Root>; using Dialog backdrop/content directly as Transition.Child without the wrapping Transition (older Headless UI v1 pattern); accidentally replacing the parent <Transition.Root show={...}> with a plain <div> during refactor; HOC/portals that render the child outside the provider tree.

Common situations: Migrating Headless UI v0 → v1 code where Dialog + Transition composition changed; wrapping Transition children in an extra element that breaks the provider chain; copy-pasting a Transition.Child snippet into a component that lacks the Transition wrapper.

Related errors


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