tailwindlabs/headlessui · error · Error

Did you forget to passthrough the `ref` to the actual DOM no

Error message

Did you forget to passthrough the `ref` to the actual DOM node?

What it means

When a Transition.Child is visible and the server handoff is complete, Headless UI applies transitions to the DOM node referenced by container (the forwarded ref). If container.current is still null at that point, the ref was never attached to a real DOM node — usually because a custom `as` component didn't forward its ref. The throw is a debug aid pinpointing the broken ref chain.

Source

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

    // Make sure that we are visible
    if (show && treeState !== TreeStates.Visible) {
      setState(TreeStates.Visible)
      return
    }

    return match(treeState, {
      [TreeStates.Hidden]: () => unregister(container),
      [TreeStates.Visible]: () => register(container),
    })
  }, [treeState, container, register, unregister, show, strategy])

  let ready = useServerHandoffComplete()

  useIsoMorphicEffect(() => {
    if (!requiresRef) return

    if (ready && treeState === TreeStates.Visible && container.current === null) {
      throw new Error('Did you forget to passthrough the `ref` to the actual DOM node?')
    }
  }, [container, treeState, ready, requiresRef])

  // Skipping initial transition
  let skip = initial && !appear
  let immediate = appear && show && initial

  let isTransitioning = useRef(false)

  let nesting = useNesting(() => {
    // When all children have been unmounted we can only hide ourselves if and
    // only if we are not transitioning ourselves. Otherwise we would unmount
    // before the transitions are finished.
    if (isTransitioning.current) return

    setState(TreeStates.Hidden)
    unregister(container)
  }, parentNesting)

View on GitHub (pinned to eea57cf46f)

Solutions

  1. Wrap the custom `as` component in React.forwardRef and spread the ref onto its root DOM element
  2. Use as={Fragment} and put className/transition classes on an inner plain element instead
  3. Pass the transition classes manually via className on your own wrapper and skip `as`
  4. If using a third-party wrapper, upgrade it or check that it forwards refs to Headless UI components

Example fix

// before
function Panel(props) {
  return <div {...props} /> // ref dropped
}
<Transition.Child as={Panel} ... />

// after
const Panel = React.forwardRef((props, ref) => (
  <div {...props} ref={ref} />
))
<Transition.Child as={Panel} ... />
Defensive patterns

Strategy: validation

Validate before calling

const forwardsRef = (Component) =>
  Component === Fragment ||
  typeof Component === 'string' ||
  Component?.$$typeof === Symbol.for('react.forward_ref');

const As = forwardsRef(MyComponent) ? MyComponent : 'div';
<Transition.Child as={As} ...>

Type guard

const isForwardRefComponent = (c) =>
  typeof c === 'function' && c.$$typeof === Symbol.for('react.forward_ref');

Prevention

When it happens

Trigger: Passing as={MyComponent} to Transition/Transition.Child where MyComponent doesn't forward the ref (class components, or function components without React.forwardRef); wrapping the child in a custom component that accepts but ignores/spreads away the ref; libraries like styled-components/emotion versions or HOCs that swallow refs; React 19 ref-as-prop components that accidentally shadow the ref.

Common situations: Using a custom component or styled wrapper as the `as` prop; migrating to wrappers that forgot forwardRef; using third-party UI wrappers around Headless UI that don't forward refs.

Related errors


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