tailwindlabs/headlessui · error · Error

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

Error message

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

What it means

Headless UI Vue's <Transition> requires an explicit boolean `show` prop to know when to toggle visibility. A `watchEffect` asserts that `show.value` is strictly `true` or `false`; anything else (undefined, null, a string, a number) throws, because the internal visibility TreeState can't be derived from a non-boolean.

Source

Thrown at packages/@headlessui-vue/src/components/transitions/transition.ts:426

    beforeEnter: () => true,
    afterEnter: () => true,
    beforeLeave: () => true,
    afterLeave: () => true,
  },
  setup(props, { emit, attrs, slots }) {
    let usesOpenClosedState = useOpenClosed()

    let show = computed(() => {
      if (props.show === null && usesOpenClosedState !== null) {
        return (usesOpenClosedState.value & State.Open) === State.Open
      }

      return props.show
    })

    watchEffect(() => {
      if (![true, false].includes(show.value)) {
        throw new Error('A <Transition /> is used but it is missing a `:show="true | false"` prop.')
      }
    })

    let state = ref(show.value ? TreeStates.Visible : TreeStates.Hidden)

    let nestingBag = useNesting(() => {
      state.value = TreeStates.Hidden
    })

    let initial = ref(true)
    let transitionBag = {
      show,
      appear: computed(() => props.appear || !initial.value),
    }

    onMounted(() => {
      watchEffect(() => {
        initial.value = false

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Always pass a strict boolean: `<Transition :show="isOpen">` where `isOpen` is `ref(false)` or computed to boolean.
  2. If the source value may be undefined, coerce it: `:show="Boolean(open)"` or `:show="!!open"`.
  3. Initialize the state ref as `ref(false)`, not `ref(null)` or `ref(undefined)`.
  4. Remember `show="true"` (string) is wrong in Vue; use `:show="true"`.

Example fix

<!-- before -->
<Transition show="true"> ... </Transition>
<!-- or -->
<Transition> ... </Transition>

<!-- after -->
<Transition :show="isOpen"> ... </Transition>
Defensive patterns

Strategy: validation

Validate before calling

// Vue: ensure a strict boolean before binding
<Transition :show="typeof open === 'boolean' ? open : !!open">

Type guard

const isBoolean = (v: unknown): v is boolean => typeof v === 'boolean'

Prevention

When it happens

Trigger: Using <Transition> without a `:show` binding; binding `:show="undefined"` because the data isn't loaded yet; passing `show="false"` as a string attribute instead of `:show="false"`; binding to a non-boolean ref or a value that starts as null.

Common situations: Copy-pasting <Transition> from docs but forgetting the show prop; using `v-if`-style truthy values (e.g., a count or object) as `show`; async state where `open` is initially undefined; passing string literals without the `:` binding prefix in Vue.

Related errors


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