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
- Wrap the Transition.Child elements in <Transition.Root show={isOpen}> (or <Transition show={isOpen}>)
- If you don't need a transition, replace Transition.Child with a plain element or Dialog backdrop/panel directly
- If you meant a standalone transition, use <Transition> itself instead of <Transition.Child>
- 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
- Keep Transition.Root and its Transition.Children in the same component/file
- Codemod/lint against bare Transition.Child without a Transition ancestor
- If unsure, use plain elements instead of Transition.Child
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
- A <TransitionChild /> is used but it is missing a parent <Tr
- Did you forget to passthrough the `ref` to the actual DOM no
- A <Transition /> is used but it is missing a `show={true | f
- You must wrap your component in a <StableCollection>
- Passing props on "Fragment"! The current component <${name}
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/2ea490fe63923046.
Report an issue: GitHub.