A panel that slides in from the edge of the screen and can be swiped away.
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 the primitive from Animate UI instead of Base UI. Everything else stays the same, types included (Drawer.Popup.Props, Drawer.Root.SnapPoint…).
import { Drawer } from '@animate-ui/react/primitives/base-ui/drawer';
export function DailyGoals() {
return (
<Drawer.Root>
<Drawer.Trigger>Daily goals</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Backdrop />
<Drawer.Viewport>
<Drawer.Popup>
<Drawer.Content>
<Drawer.Title>Daily goals</Drawer.Title>
<Drawer.Description>Swipe down to dismiss.</Drawer.Description>
<Drawer.Close>Done</Drawer.Close>
</Drawer.Content>
</Drawer.Popup>
</Drawer.Viewport>
</Drawer.Portal>
</Drawer.Root>
);
}Using the @base-ui/react umbrella import instead? The barrel mirrors it: import { Drawer } from '@animate-ui/react/primitives/base-ui'.
shadcn/ui's React Aria Drawer is built on Base UI's Drawer too: animate-ui animate drawer animates it the same way. shadcn/ui's Radix UI Drawer is built on Vaul instead: Animate UI replaces it with the Radix UI Drawer, which has Vaul's API.
Animation
Animate UI v1 had no drawer: Animate UI's motion, with Base UI's swipe.
- Popup slides in from its edge (
swipeDirection, a full size of its own), opaque, a sheet sliding in rather than a fade, in 0.4s ofcubic-bezier(0.34, 1.2, 0.64, 1): it answers at once, goes a hair past its rest (about 1% of its size, which shadcn/ui's bleed below it covers), then settles back. A hint of bounce, where Vaul's curve zips in, then stops. - Closed (Escape, a click outside, a Close), it leaves quickly, seen speeding off its edge (0.16s on an ease-in,
cubic-bezier(0.4, 0, 1, 1)), so it never reads as a constant slide and its last pixels don't crawl out. - Its shadow lifts off the page with it: its starting and ending styles drop it, so it grows from none as the popup slides in, on the same transition, and fades as it leaves rather than riding out at full strength.
- While you drag it, it follows the finger on the same frame: no transition runs during a swipe. Let go slowly and it eases back; flick it and it's thrown: it keeps the finger's speed to its edge, linear, over Base UI's
--drawer-swipe-strengthshare of 360ms (the time the rest of the way takes at that speed). - Backdrop comes in and clears exactly like a dialog's overlay: its tint deepens while its backdrop blur grows from 0, in 400ms on a soft ease-out both ways, so the page behind blurs progressively (fading its opacity would cross-fade the sharp page with an already blurred one). It follows the swipe in between. The drawer waits for it: Base UI unmounts a closed drawer once its Popup's own transitions have played, which cut a slower backdrop short and snapped the page back sharp, so the Popup holds until its Backdrop has cleared. Its classes' color and blur are read once it mounts; over a background image (a gradient), it fades its opacity instead.
- Snap points, nested drawers and
swipeDirectionwork as in Base UI. - CSS-driven, not Motion: Base UI drives the swipe through CSS variables and waits for CSS transitions before unmounting, so a JavaScript animation would fight the finger. The primitive brings its own small stylesheet (zero specificity) and the timing as inline variables: a spring becomes the
linear()curve Motion itself would use, a tween keeps its cubic-bezier, andAnimateUIProvider temporeaches it. It reads shadcn/ui's--translate-x,--translate-y,--stack-scaleand--closed-transformwhen they're set. - Under reduced motion, the drawer fades in place; a swipe dismissal still leaves along the swipe.
API Reference
Every part takes exactly the props of Base UI's Drawer. Animate UI adds the animation props below.
Root, Provider, Trigger, Portal, Viewport, SwipeArea, Content, Title, Description, Close, Indent, IndentBackground, VirtualKeyboardProvider
Base UI props only. They are not animated.
Popup
Base UI props, plus:
| Prop | Type | Default |
|---|---|---|
transition? | Transition | { duration: 0.4, ease: [0.34, 1.2, 0.64, 1] } |
exitTransition? | Transition | { duration: 0.16, ease: [0.4, 0, 1, 1] } |
Backdrop
Base UI props, plus:
| Prop | Type | Default |
|---|---|---|
transition? | Transition | { duration: 0.4, ease: [0.215, 0.61, 0.355, 1] } |
exitTransition? | Transition | transition |