AlertDialog with motion: it grows out of its button, its icon pops in, a click outside shakes it because it needs an answer, and confirm can be a press-and-hold.
$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-alert-dialog.jsonInstallation
$ pnpm dlx shadcn@latest add https://dashral.com/r/motion-alert-dialog.jsonAdds components/dashral/motion/alert-dialog.tsx, the Alert Dialog it builds on (in components/dashral/), motion and the motion files every motion component shares. With the registry in your components.json:
$ pnpm dlx shadcn@latest add @dashral/motion-alert-dialogUsage
The same as Alert Dialog, imported from components/dashral/motion/: swap the import and it moves.
import { AlertDialog } from "@/components/dashral/motion/alert-dialog"
<AlertDialog
trigger={<Button color="destructive">Delete project</Button>}
title="Delete this project?"
color="destructive"
confirmLabel="Delete"
onConfirm={() => api.projects.delete(id)}
/>Examples
Compared with Default
Fades and zooms in from 95% in the middle of the screen; a click outside does nothing.
Motion
Grows out of its button and shrinks back into it; its icon pops in (a destructive one wiggles); a click outside shakes it; holdToConfirm makes confirm a press-and-hold.
How the two editions differ in general, and which to use where: Motion.
API reference
Every prop of Alert Dialog, and every attribute of the element it renders. What the Motion edition adds is marked Motion.
AlertDialog
Also takes every prop of Radix Alert Dialog(opens in a new tab).
| Prop | Type | Default |
|---|---|---|
holdToConfirm | boolean | number
| false |
trigger | ReactElementWhat opens it, e.g. a Delete button. Leave it out to open it with open / onOpenChange. | — |
title | ReactNodeRequired: the question, which names the dialog. | — |
description | ReactNodeWhat happens if they go ahead. | — |
children | ReactNodeMore between the text and the buttons, e.g. a field to type a name. | — |
icon | ReactNodeIn a tinted circle beside the text. | — |
color | "primary" | "destructive"The confirm button's (and the icon's) color; destructive for deleting. | "primary" |
confirmLabel / cancelLabel | ReactNodeThe buttons' labels (translated by default). | "Continue" / "Cancel" |
onConfirm | () => void | Promise<unknown>Runs on confirm. A promise keeps the dialog open with the button showing it's working; it closes when the promise resolves, and stays if it rejects. | — |
loadingText | ReactNodeBeside the spinner while onConfirm's promise runs. | — |
confirmDisabled | booleanKeeps confirm off, e.g. until a name is typed. | false |
open / defaultOpen / onOpenChange | Controlled or initial state. | — |
classNames | { title, description, footer }Classes for each part. | — |
className | stringThe panel's classes. | — |
Motion
- It grows from 60% out of the button that opened it on a quick spring, and shrinks back toward it as it closes.
- The icon pops in after it; a destructive icon gives a short wiggle.
- A click outside doesn't close it, so the panel shakes sideways a few pixels: it needs an answer.
- Confirm is the motion Button: it shows it's working while onConfirm's promise runs, and with holdToConfirm it fills while held.
- With reduced motion it only fades and nothing shakes or wiggles.