tailwindlabs/headlessui · error · Error
A <TransitionChild /> is used but it is missing a parent <Tr
Error message
A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.
What it means
TransitionChild in @headlessui-vue injects a TransitionContext that only a parent TransitionRoot provides (show/appear state, register/unregister callbacks). If inject() returns null the component is being used standalone, which is unsupported, so it throws immediately in setup.
Source
Thrown at packages/@headlessui-vue/src/components/transitions/transition.ts:61
show: Ref<boolean>
appear: Ref<boolean>
}
let TransitionContext = Symbol('TransitionContext') as InjectionKey<TransitionContextValues | null>
enum TreeStates {
Visible = 'visible',
Hidden = 'hidden',
}
function hasTransitionContext() {
return inject(TransitionContext, null) !== null
}
function useTransitionContext() {
let context = inject(TransitionContext, null)
if (context === null) {
throw new Error('A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.')
}
return context
}
function useParentNesting() {
let context = inject(NestingContext, null)
if (context === null) {
throw new Error('A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.')
}
return context
}
interface NestingContextValues {
children: Ref<{ id: ID; state: TreeStates }[]>
register: (id: ID) => () => voidView on GitHub (pinned to eea57cf46f)
Solutions
- Wrap the TransitionChild in <TransitionRoot :show="isOpen">...</TransitionRoot>.
- If you don't need nesting, use the self-contained <Transition> component instead of TransitionChild.
- Verify the TransitionRoot ancestor is the same package/version instance so the context symbol matches.
Example fix
// before <TransitionChild as="template" enter="transition" >...</TransitionChild> // after <TransitionRoot :show="isOpen"> <TransitionChild as="template" enter="transition">...</TransitionChild> </TransitionRoot>
Defensive patterns
Strategy: validation
Validate before calling
// Structure check before shipping: TransitionChild must be inside TransitionRoot's slot <TransitionRoot :show="isOpen"> <TransitionChild as="template" enter="...">...</TransitionChild> </TransitionRoot>
Try / catch
try { render(Template) } catch (e) { if (e instanceof Error && e.message.includes('missing a parent <TransitionRoot />')) { /* swap TransitionChild for the standalone Transition */ } else throw e } Prevention
- Use TransitionChild only inside TransitionRoot; otherwise use Transition.
- Keep the TransitionRoot/TransitionChild pair in the same template to make the relationship obvious.
- Watch for Vue warnings about injected context during development to catch bad nesting early.
When it happens
Trigger: Rendering <TransitionChild> outside a <TransitionRoot>; renaming/wrapping TransitionRoot with a custom component that breaks provide/inject; using TransitionChild where Transition (the standalone wrapper) was intended.
Common situations: Copy-pasting TransitionChild-based animation snippets without the enclosing TransitionRoot; splitting transition markup into child components during refactor; upgrading from v1 where composition rules differed.
Related errors
- Missing parent
- Did you forget to passthrough the `ref` to the actual DOM no
- A <Transition /> is used but it is missing a `:show="true |
- A <Transition.Child /> is used but it is missing a parent <T
- You forgot to provide an `open` prop to the `Dialog`.
AI-assisted analysis of tailwindlabs/headlessui@eea57cf46f (2026-08-28).
Data as JSON: /api/errors/b61873c7d3133106.
Report an issue: GitHub.