Dialog
Portal modal with blurred backdrop, spring scale-in, focus trap, Esc and scroll lock.
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/ui/dialog";export function EditProfile() { return ( <Dialog> <DialogTrigger variant="outline">Edit profile</DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription>Changes apply to your account.</DialogDescription> </DialogHeader> <DialogFooter> <DialogClose>Cancel</DialogClose> </DialogFooter> </DialogContent> </Dialog> );}<Dialog>
State root. Controlled (open + onOpenChange) or uncontrolled.
children*ReactNodeDialogTriggerandDialogContent.defaultOpendefaultfalsebooleanOpen at first render (uncontrolled).
onOpenChange(open: boolean) => voidCalled on the trigger, Esc, backdrop click or
DialogClose.openbooleanControlled state; pair with
onOpenChange.
| Prop | Type | Default |
|---|---|---|
children*
| ReactNode | — |
defaultOpenOpen at first render (uncontrolled). | boolean | false |
onOpenChangeCalled on the trigger, Esc, backdrop click or | (open: boolean) => void | — |
openControlled state; pair with | boolean | — |
<DialogTrigger>
childrenReactNodeLabel; its width is reserved so loading and success never resize the button.
confirmLabelReactNodeTwo-step action: first click swaps the label to this, second click runs
onClick.confirmTimeoutnumberMs before an armed confirm step resets. Also resets on blur or Esc.
loadingbooleanDisables the button and shows a spinner before the label.
loadingLabelReactNodeLabel shown while loading, with the spinner. Defaults to
children.size"default" | "sm" | "lg" | "icon"Height and padding; "icon" is square, so give it an
aria-label.statusButtonStatusIn-place feedback:
loadingspins,successswaps the label tosuccessLabel.successLabelReactNodeText shown on
status="success", with a check icon. Defaults to "Done".swapSwapDirectionWhere the old label slides out to on a swap. Default "left".
variant"default" | "secondary" | "outline" | "ghost" | "destructive"Fill and emphasis: one primary action per view,
destructivefor irreversible ones.
| Prop | Type | Default |
|---|---|---|
childrenLabel; its width is reserved so loading and success never resize the button. | ReactNode | — |
confirmLabelTwo-step action: first click swaps the label to this, second click runs | ReactNode | — |
confirmTimeoutMs before an armed confirm step resets. Also resets on blur or Esc. | number | — |
loadingDisables the button and shows a spinner before the label. | boolean | — |
loadingLabelLabel shown while loading, with the spinner. Defaults to | ReactNode | — |
sizeHeight and padding; "icon" is square, so give it an | "default" | "sm" | "lg" | "icon" | — |
statusIn-place feedback: | ButtonStatus | — |
successLabelText shown on | ReactNode | — |
swapWhere the old label slides out to on a swap. Default "left". | SwapDirection | — |
variantFill and emphasis: one primary action per view, | "default" | "secondary" | "outline" | "ghost" | "destructive" | — |
Also accepts every Button prop.
<DialogClose>
childrenReactNodeLabel; its width is reserved so loading and success never resize the button.
confirmLabelReactNodeTwo-step action: first click swaps the label to this, second click runs
onClick.confirmTimeoutnumberMs before an armed confirm step resets. Also resets on blur or Esc.
loadingbooleanDisables the button and shows a spinner before the label.
loadingLabelReactNodeLabel shown while loading, with the spinner. Defaults to
children.size"default" | "sm" | "lg" | "icon"Height and padding; "icon" is square, so give it an
aria-label.statusButtonStatusIn-place feedback:
loadingspins,successswaps the label tosuccessLabel.successLabelReactNodeText shown on
status="success", with a check icon. Defaults to "Done".swapSwapDirectionWhere the old label slides out to on a swap. Default "left".
variantdefault"outline""default" | "secondary" | "outline" | "ghost" | "destructive"Fill and emphasis: one primary action per view,
destructivefor irreversible ones.
| Prop | Type | Default |
|---|---|---|
childrenLabel; its width is reserved so loading and success never resize the button. | ReactNode | — |
confirmLabelTwo-step action: first click swaps the label to this, second click runs | ReactNode | — |
confirmTimeoutMs before an armed confirm step resets. Also resets on blur or Esc. | number | — |
loadingDisables the button and shows a spinner before the label. | boolean | — |
loadingLabelLabel shown while loading, with the spinner. Defaults to | ReactNode | — |
sizeHeight and padding; "icon" is square, so give it an | "default" | "sm" | "lg" | "icon" | — |
statusIn-place feedback: | ButtonStatus | — |
successLabelText shown on | ReactNode | — |
swapWhere the old label slides out to on a swap. Default "left". | SwapDirection | — |
variantFill and emphasis: one primary action per view, | "default" | "secondary" | "outline" | "ghost" | "destructive" | "outline" |
Also accepts every Button prop.
<DialogContent>
Portal + blurred backdrop + spring scale-in. Esc/backdrop close, focus trap, scroll lock.
children*ReactNodeHeader, body and footer.
alertdefaultfalsebooleanConfirm-a-destructive-action mode: role="alertdialog", backdrop click does not close.
showClosedefault!alertbooleanClose button in the corner. Off by default for
alertdialogs.
| Prop | Type | Default |
|---|---|---|
children*Header, body and footer. | ReactNode | — |
alertConfirm-a-destructive-action mode: role="alertdialog", backdrop click does not close. | boolean | false |
showCloseClose button in the corner. Off by default for | boolean | !alert |
<DialogHeader>
Also accepts every <div> prop (none of its own).
<DialogFooter>
Also accepts every <div> prop (none of its own).
<DialogTitle>
Also accepts every <h2> prop (none of its own).
<DialogDescription>
Also accepts every <p> prop (none of its own).
useDialog()
useDialog() => Ctx
useModal()
Modal behavior shared by Dialog and Drawer: focus first field, trap Tab, restore focus, lock page scroll, close on Esc (topmost modal only). Attach the returned ref to the panel.
useModal() => RefObject<HTMLDivElement | null>
- type Ctx
- { open: boolean; setOpen: (open: boolean) => void; titleId: string; descId: string }
* required. Every component also takes className.