A select displays a collapsible list of options and allows a user to select one of them.
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, ListBox, ListBoxItem and SelectionIndicator. Everything else stays the same.
import {
Button,
ListBox,
ListBoxItem,
Popover,
Select,
SelectionIndicator,
SelectValue,
} from '@animate-ui/react/primitives/react-aria';
export function FontPicker() {
return (
<Select aria-label="Font" defaultValue="geist">
<Button>
<SelectValue />
</Button>
<Popover placement="bottom start" offset={4}>
<ListBox>
<ListBoxItem id="inter" textValue="Inter">
Inter
<SelectionIndicator>✓</SelectionIndicator>
</ListBoxItem>
<ListBoxItem id="geist" textValue="Geist">
Geist
<SelectionIndicator>✓</SelectionIndicator>
</ListBoxItem>
<ListBoxItem id="lora" textValue="Lora">
Lora
<SelectionIndicator>✓</SelectionIndicator>
</ListBoxItem>
</ListBox>
</Popover>
</Select>
);
}Check animation
The chosen item's check draws itself while the previous one erases itself. With animation="pop" on every item's SelectionIndicator, the check glides from the previous item to the chosen one instead (a first choice pops it in from half its size):
<ListBoxItem id="geist" textValue="Geist">
Geist
<SelectionIndicator animation="pop">✓</SelectionIndicator>
</ListBoxItem>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 Select has no Animate UI v1 counterpart: its list opens in Animate UI's Popover, which moves like v1's Dropdown Menu content under a select.
- Popover: the list fades and scales in from 0.9, from the trigger (
--trigger-anchor-point), in 220ms, and closes the same way before it unmounts. Reopening while it closes picks up from where it is. - ListBoxItem: 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 selects. React Aria focuses the selected item when the list opens, so the highlight starts on it. 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 heavy render for instance, doesn't stall it. When the pointer leaves the list, it stays on the last item. It wears the item's own focused background (data-focused:bg-accent…), read from your styles: the item's background is cleared while it's focused; its text colors stay. The highlight is rendered through the item'srenderprop: your ownrenderstill works. The ListBox is its own stacking context (isolation: isolate), so the highlight goes under the items' content, above the list's background. - SelectionIndicator (the check inside an item): the chosen item's check draws itself like a Checkbox's check (its stroke and opacity in 0.3s on Motion's easeOut, at once) while the previous one erases itself, and the list holds still for 250ms before it closes, so the change is seen nearly done before the list fades out. With
animation="pop"(orcheckAnimation="pop"on anAnimateUIProvider), the check glides there instead, on the highlight's spring (stiffness 500, damping 40), and the list holds for 150ms, and a first choice pops it in from half its size out of a 2px blur (a spring, stiffness 500, damping 24). The demo's Controls switch between the two. Choosing the item already chosen, and closing any other way (Escape, a click outside), don't wait. WithselectionMode="multiple", the list stays open: a selected item's check draws itself, and erases itself when it's deselected; withpop, it pops in from half its size and shrinks back out in 0.15s. Only a change of selection animates the checks: opening the list shows them at rest. shadcn/ui renders the check asisSelected ? <Icon /> : null, which can't animate its leaving: render aSelectionIndicatoron every item instead, as the demo does (a check rendered only on the selected item goes at once, though withpopit still glides).SelectValuerenders the chosen item's content again in the trigger: the indicator renders nothing there (React Aria's own would throw outside the list). Outside Animate UI's ListBox and Menu (in a Tab, a ToggleButton…) or with your ownrender, it's React Aria's own. - Opening from the keyboard animates the same way.
- Open from the start (
defaultOpen), the list plays its entrance on page load. - Under reduced motion, the list 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
Select, SelectValue, ListBoxSection, Header and the other components take React Aria props only. They are not animated.
Popover
React Aria props, plus:
| Prop | Type | Default |
|---|---|---|
transition? | Transition | { duration: 0.22, ease: [0.215, 0.61, 0.355, 1] } |
exitTransition? | Transition | transition |
ListBox
React Aria props only. It holds its items' highlight and its checks (a single selection's glides between them with pop).
ListBoxItem
React Aria props only. It takes part in the highlight; it needs nothing more.
SelectionIndicator
React Aria props, plus:
| Prop | Type | Default |
|---|---|---|
animation? | 'draw' | 'pop' | the closest AnimateUIProvider's checkAnimation, else 'draw' |
transition? | Transition | draw: 0.3s on Motion's easeOut both ways (a Checkbox's, without its delay); pop: a spring (stiffness 500, damping 24) in, 0.15s out, and the highlight's spring (stiffness 500, damping 40) for the glide |