A popup that opens on top of the entire page. Also the primitive under shadcn/ui's Sheet.
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 (Dialog.Popup.Props, Dialog.Root.ChangeEventDetails…).
import { Dialog } from '@animate-ui/react/primitives/base-ui/dialog';
export function EditProfile() {
return (
<Dialog.Root>
<Dialog.Trigger>Edit profile</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Backdrop />
<Dialog.Popup>
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
Make changes to your profile here.
</Dialog.Description>
<Dialog.Close>Save</Dialog.Close>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
);
}Using the @base-ui/react umbrella import instead? The barrel mirrors it: import { Dialog } from '@animate-ui/react/primitives/base-ui'.
Sheet
shadcn/ui's Sheet is built on this primitive: with data-side on its popup, it slides in from that edge instead of tipping in. See Sheet.
Animation
Animate UI v1's dialog motion, with a smoother overlay and a quicker exit.
- Backdrop fades in from nothing to what its classes set: its background color deepens while its backdrop filter grows, so the page behind blurs progressively from 0 (fading its opacity would cross-fade the sharp page with an already blurred one). Over 400ms on a soft ease-out, along with the popup's fade, and it leaves the same way.
- Popup tips forward into place, around its center: it starts from
perspective(500px) rotateX(-20deg) scale(0.8), transparent and blurred (4px), and comes upright and sharp on a spring (stiffness 150, damping 25), fading in ahead of it with the backdrop (400ms on a soft ease-out): it's there first, then it settles. Dismissed, it goes back the way it came, at once, quicker and speeding away (0.2s, a steady push from half its average speed to one and a half), out of sight just before the backdrop has cleared, then unmounts. frompicks the edge the popup tips from, as in v1:topby default (rotateX(-20deg)),bottom(rotateX(20deg)),left(rotateY(20deg)) orright(rotateY(-20deg)) (a sheet, withdata-side, slides from its side instead).- On the backdrop,
exitTransitiondefaults totransition: changetransitionand both directions follow, as in v1. A dialog's or a sheet's own exit is the quicker one above unless you setexitTransition. - Base UI decides a closed popup is done before Motion's exit has started. The Portal keeps Base UI's portal mounted and the popup visible until the exit is over; clicks go through to the page as soon as it closes.
- Reopening while it closes picks up from where it is.
eventDetails.cancel()inonOpenChangekeeps it open, detached triggers (handle) open it, and opening from the keyboard animates the same way.- With
keepMountedon the Portal, the closed popup stays in the DOM: it plays its exit, then Base UI hides it. - Your own
renderelement (or function) on the Backdrop or the Popup is made animatable rather than replaced. - Under reduced motion, the popup fades and un-blurs without tilting, scaling or sliding.
AnimateUIProvider temposcales every duration and spring.
API Reference
Every part takes exactly the props of Base UI's Dialog. Animate UI adds the animation props below.
Root, Trigger, Portal, Viewport, Title, Description, Close
Base UI props only. They are not animated.
Backdrop
Base UI props, plus:
| Prop | Type | Default |
|---|---|---|
transition? | Transition | { duration: 0.4, ease: [0.215, 0.61, 0.355, 1] } |
exitTransition? | Transition | transition |
Popup
Base UI props, plus:
| Prop | Type | Default |
|---|---|---|
from? | 'top' | 'bottom' | 'left' | 'right' | 'top' |
transition? | Transition | { type: 'spring', stiffness: 150, damping: 25, opacity: { duration: 0.4, ease: [0.215, 0.61, 0.355, 1] } }; with data-side: { duration: 0.4, ease: [0.215, 0.61, 0.355, 1] } |
exitTransition? | Transition | { duration: 0.2, ease: [0.33, 0.17, 0.67, 0.5] } |