Displays a list of actions or options that a user can choose, triggered by a button.
Installation
Install the packages
Add the shadcn/ui component
Animate it
The CLI finds your shadcn/ui components through components.json, swaps their primitive imports for Animate UI's and removes the CSS animation classes Motion replaces.
Usage
Import from Animate UI instead of react-aria-components: it re-exports all of React Aria, with an animated Popover, Menu, MenuSection, MenuItem and SelectionIndicator. Everything else stays the same.
import {
Button,
Menu,
MenuItem,
MenuTrigger,
Popover,
} from '@animate-ui/react/primitives/react-aria';
export function AccountMenu() {
return (
<MenuTrigger>
<Button>Account</Button>
<Popover placement="bottom start" offset={4}>
<Menu>
<MenuItem>Profile</MenuItem>
<MenuItem>Billing</MenuItem>
<MenuItem>Log out</MenuItem>
</Menu>
</Popover>
</MenuTrigger>
);
}Check animation
An item of a selection (selectionMode on the Menu or a MenuSection) shows its check in a SelectionIndicator: it draws itself when the item is selected, and erases itself when it's deselected (a single selection's while the new one draws). With animation="pop", it pops in from half its size and shrinks back out instead, and a single selection's check glides to the newly selected item:
import {
Button,
Menu,
MenuItem,
MenuSection,
MenuTrigger,
Popover,
SelectionIndicator,
} from '@animate-ui/react/primitives/react-aria';
export function ViewMenu() {
return (
<MenuTrigger>
<Button>View</Button>
<Popover placement="bottom start" offset={4}>
<Menu>
<MenuSection
selectionMode="multiple"
defaultSelectedKeys={['status-bar']}
>
<MenuItem id="status-bar">
<SelectionIndicator animation="pop">
<CheckIcon />
</SelectionIndicator>
Status bar
</MenuItem>
</MenuSection>
<MenuSection selectionMode="single" defaultSelectedKeys={['bottom']}>
<MenuItem id="top">
<SelectionIndicator animation="pop">
<CheckIcon />
</SelectionIndicator>
Top
</MenuItem>
<MenuItem id="bottom">
<SelectionIndicator animation="pop">
<CheckIcon />
</SelectionIndicator>
Bottom
</MenuItem>
</MenuSection>
</Menu>
</Popover>
</MenuTrigger>
);
}The same demo with checkAnimation="pop" (its Controls switch it back to draw):
To pop every check of your app at once (checkboxes, menus, selects, comboboxes), set checkAnimation="pop" on an AnimateUIProvider instead.
Animation
React Aria's menus open in a Popover, so they get the animated Popover, which moves like Animate UI v1's Dropdown Menu content under a menu:
- The menu fades and scales in from 0.9, from the trigger (
--trigger-anchor-point), in 220ms, and closes the same way before it unmounts. - Submenus (
SubmenuTrigger) open in a Popover too, and move like their menu. - MenuItem: one highlight glides behind the focused item (the one React Aria marks
data-focused) on a spring (stiffness 500, damping 40), following the pointer and the arrow keys alike, like v1's menu highlight and the Radix UI and Base UI menus. Moving to another item, it carries on from where it is, at its speed: however fast the pointer or the arrow keys go, there's one highlight on screen. It only fades in (200ms, on CSS'sease) when it appears from nothing, and its color changes the same way when the item's highlighted background differs from the previous one's. It glides on the compositor (a Web Animation oftransform), so a busy main thread, a submenu opening for instance, doesn't stall it. Into a submenu or back out of it, each menu clips its own part, as in v1: the previous surface glides along with the new one, the same box on screen, so the two make one highlight crossing from one menu into the other; the entering part fades in as it glides. It goes into submenus, whoseMenushares the highlight of its root menu, and stays on the last item when the pointer leaves the menu. It wears the item's own focused background (focus:bg-accent,data-focused:bg-accent…), read from your styles: the item's background is cleared while it's focused, or while its submenu is open; its text colors stay. The highlight is rendered through the item'srenderprop: your ownrenderstill works. TheMenuis its own stacking context (isolation: isolate), so the highlight goes under the items' content, above the menu's background. Inside a paddedPopover, theMenureaches into the padding (a negative margin and as much padding, so its items don't move): it clips where the popover's padding ends, as the Radix UI and Base UI menus do, and the highlight crossing into a submenu isn't cut between the two. - Open from the start (
defaultOpenon theMenuTrigger), the menu plays its entrance on page load, like v1. - SelectionIndicator (the check inside an item, the same as a ListBox's): an item's check draws itself like a Checkbox's check (its stroke and opacity in 0.3s) when the item is selected, and erases itself when it's deselected; with
selectionMode="single"(on theMenuor aMenuSection), selecting another item erases the old check while the new one draws itself. Only selecting and deselecting animate: opening the menu shows its checks at rest, and closing it leaves them as they are. Withanimation="pop"(orcheckAnimation="pop"on anAnimateUIProvider), checks pop instead: in from half their size out of a 2px blur (a spring, stiffness 500, damping 24), and back into their blur in 0.15s; a single selection's check glides from the item it leaves to the newly selected one, on the highlight's spring (stiffness 500, damping 40), between the items of itsMenuorMenuSection. The demo's Controls switch between the two. Choosing an item that closes the menu (with the pointer or Enter) holds the menu still while its check changes (250ms while it draws itself, 150ms while it pops or glides), then it fades out; Escape and outside clicks don't wait. shadcn/ui renders the check asisSelected ? <Icon /> : null, which can't animate its leaving: render it in aSelectionIndicatoron every item instead, as the demo does. - Under reduced motion, the menu only fades and doesn't hold; the highlight jumps from item to item instead of gliding (it still fades in when it appears); the checks only fade (a single selection's doesn't glide).
API Reference
Popover takes exactly the props of React Aria's Popover, plus transition and exitTransition (see the Popover page).
MenuTrigger, SubmenuTrigger and the other components take React Aria props only. They are not animated.
Menu
React Aria props only. It holds its items' highlight; a submenu's Menu shares its root menu's.
MenuSection
React Aria props only. With its own selectionMode="single", its items' check glides between them.
MenuItem
React Aria props only. It takes part in the highlight; it needs nothing more.
SelectionIndicator
React Aria props, plus animation and transition (see the Select page). Inside an item of Animate UI's Menu (or ListBox), it's animated; anywhere else, it's React Aria's own.