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) => () => void

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Wrap the TransitionChild in <TransitionRoot :show="isOpen">...</TransitionRoot>.
  2. If you don't need nesting, use the self-contained <Transition> component instead of TransitionChild.
  3. 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

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


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