ShadCN/Radix Dropdown Open on Hover Tutorial (Fix "Flicker")

Updated

2 min read

Happy Sunday everyone!

Today I debugged an annoying UI bug when using shadcn / Radix UI's Dropdown Menu component.

The Bug

I wanted the dropdown menu on Featurable to open when hovering (my thinking was: why make the user do extra clicks?) and stay open while interacting with the dropdown.

My first attempt was to wrap the DropdownMenu in a <div>:

HeaderLink.tsx
tsx
const HeaderLink = () => {
    const [dropdownOpen, setDropdownOpen] = useState(false);

    return (
        <div
           onMouseLeave={() => {
               // Close when leaving the trigger or dropdown menu
               setDropdownOpen(false);
           }}
       >
           <DropdownMenu
               open={dropdownOpen}
               onOpenChange={setDropdownOpen}
           >
               <DropdownMenuTrigger
                   onMouseEnter={() => setDropdownOpen(true)}
               >
                   Open me!
               </DropdownMenuTrigger>
               <DropdownContent>
                   {/* Dropdown content goes here */}
               </DropdownContent>
           </DropdownMenu>
       </div>
    );
}

This worked! But it caused an annoying flicker:

If you were to console.log the mouseenter and mouseleave events, you'd see:

Entering Leaving Entering

So why was this happening?

After debugging, I noticed that upon opening the Dropdown Menu, Radix UI was causing the body tag to receive style="pointer-events: none", which prevented scrolling and any outside interaction while the dropdown was open.

Here's what was happening:

  1. Hover the trigger --> mouseenter event (1st Entering console log)
  2. Dropdown opens
  3. Body receives pointer-events: none
  4. Since the trigger (which is in the <body>) can not receive mouse events, it fires a mouseleave (Leaving console log)

The Fix

The fix is actually quite simple. Simply add modal={false} to the DropdownMenu component like so:

jsx
const HeaderLink = () => {
    const [dropdownOpen, setDropdownOpen] = useState(false);

    return (
        <div
            onMouseLeave={() => {
                // Close when leaving the trigger or dropdown menu
                setDropdownOpen(false);
            }}
        >
            <DropdownMenu
                open={dropdownOpen}
                onOpenChange={setDropdownOpen}
                modal={false} // This will prevent the dropdown menu from being a modal 
            >
                <DropdownMenuTrigger
                    onMouseEnter={() => setDropdownOpen(true)}
                >
                    Open me!
                </DropdownMenuTrigger>
                <DropdownContent>
                    {/* Dropdown content goes here */}
                </DropdownContent>
            </DropdownMenu>
        </div>
    );
}

Now when the DropdownMenuTrigger is hovered, the <body> tag will not receive pointer-events: none.

Let's see the fix in action:

It worked! Hope this quick fix helps you with this annoying UI bug when using shadcn or Radix UI.

Want to learn from my journey building online businesses? Join my newsletter.

No spam. Unsubscribe at any time.

Ryan Chiang

Meet the Author

Ryan Chiang

Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.

2025

2024

2023

© 2023-2025 Ryan Chiangryanschiang.com