import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="danger" />}>Delete Account</AlertDialogTrigger>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove your
data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
<AlertDialogClose render={<Button variant="danger" />}>Delete Account</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
);
}Installation
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
import { cn } from "@/lib/cn";
const alertDialogCreateHandle = AlertDialogPrimitive.createHandle;
function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
}
function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
}
function AlertDialogTrigger(props: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
}
type AlertDialogBackdropVariant = "opaque" | "blur" | "transparent";
function AlertDialogBackdrop({
className,
variant = "blur",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Backdrop> & {
variant?: AlertDialogBackdropVariant;
}) {
return (
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-backdrop"
className={cn(
"fixed inset-0 z-50 transition-all duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0",
variant === "opaque" && "bg-black/50",
variant === "blur" && "bg-black/30 backdrop-blur-sm",
variant === "transparent" && "bg-transparent",
className
)}
{...props}
/>
);
}
type AlertDialogPopupPosition =
| "top"
| "center"
| "bottom"
| "top-left"
| "top-right"
| "bottom-left"
| "bottom-right"
| "top-center"
| "bottom-center";
function AlertDialogViewport({
className,
position = "center",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Viewport> & {
position?: AlertDialogPopupPosition;
}) {
return (
<AlertDialogPrimitive.Viewport
data-slot="alert-dialog-viewport"
className={cn(
"fixed inset-0 z-50 flex flex-col p-4",
position === "top" && "items-center justify-start",
position === "center" && "items-center justify-center",
position === "bottom" && "items-center justify-end",
position === "top-left" && "items-start justify-start",
position === "top-right" && "items-end justify-start",
position === "bottom-left" && "items-start justify-end",
position === "bottom-right" && "items-end justify-end",
position === "top-center" &&
"grid grid-rows-[1fr_auto_4fr] justify-items-center *:data-[slot=alert-dialog-popup]:row-start-2",
position === "bottom-center" &&
"grid grid-rows-[4fr_auto_1fr] justify-items-center *:data-[slot=alert-dialog-popup]:row-start-2",
className
)}
{...props}
/>
);
}
function AlertDialogPopup({
className,
bottomStickyOnMobile = false,
showNestedParent = false,
backdropVariant = "blur",
position = "center",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Popup> & {
bottomStickyOnMobile?: boolean;
showNestedParent?: boolean;
backdropVariant?: AlertDialogBackdropVariant;
position?: AlertDialogPopupPosition;
}) {
return (
<AlertDialogPortal>
<AlertDialogBackdrop variant={backdropVariant} />
<AlertDialogViewport
position={position}
className={cn(
bottomStickyOnMobile && "max-sm:items-center max-sm:justify-end max-sm:p-0 max-sm:pt-12"
)}
>
<AlertDialogPrimitive.Popup
data-slot="alert-dialog-popup"
className={cn(
"relative flex max-h-full w-full max-w-lg origin-center scale-[calc(1-0.1*var(--nested-dialogs))] flex-col overflow-hidden rounded-2xl border bg-popover text-popover-foreground shadow-lg transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform data-nested:data-ending-style:translate-y-8 data-nested:data-starting-style:translate-y-8 data-nested-dialog-open:origin-top data-ending-style:scale-98 data-starting-style:scale-98 data-ending-style:opacity-0 data-starting-style:opacity-0",
showNestedParent
? "-translate-y-[calc(2rem*var(--nested-dialogs))] opacity-[calc(1-0.1*var(--nested-dialogs))]"
: "opacity-[calc(1-var(--nested-dialogs))]",
bottomStickyOnMobile &&
"max-sm:max-w-none max-sm:origin-bottom max-sm:rounded-b-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4",
className
)}
{...props}
/>
</AlertDialogViewport>
</AlertDialogPortal>
);
}
function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn("flex flex-col gap-2 p-6 pb-4 text-center max-sm:px-4 sm:text-left", className)}
{...props}
/>
);
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn("font-heading font-medium text-xl leading-none", className)}
{...props}
/>
);
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn("text-pretty text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function AlertDialogFooter({
className,
variant = "default",
...props
}: React.ComponentProps<"div"> & { variant?: "default" | "muted" }) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 p-4 sm:flex-row sm:justify-end sm:px-6",
variant === "default" && "pt-2 pb-6",
variant === "muted" && "border-t bg-muted/50",
className
)}
{...props}
/>
);
}
function AlertDialogClose(props: React.ComponentProps<typeof AlertDialogPrimitive.Close>) {
return <AlertDialogPrimitive.Close data-slot="alert-dialog-close" {...props} />;
}
export {
alertDialogCreateHandle,
AlertDialog,
AlertDialogPortal,
AlertDialogBackdrop,
AlertDialogBackdrop as AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogPopup,
AlertDialogPopup as AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogClose,
AlertDialogViewport,
};Usage
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";<AlertDialog>
<AlertDialogTrigger>Delete Account</AlertDialogTrigger>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove your data
from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose>Delete Account</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>Examples
With muted footer
Pass variant="muted" prop to AlertDialogFooter to render a muted footer.
import { buttonVariants } from "@zbmui/ui/components/button";
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
export function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger className={buttonVariants({ variant: "danger-soft" })}>
Delete Account
</AlertDialogTrigger>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove your
data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter variant="muted">
<AlertDialogClose className={buttonVariants({ variant: "outline" })}>
Cancel
</AlertDialogClose>
<AlertDialogClose className={buttonVariants({ variant: "danger" })}>
Delete Account
</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
);
}Controlled
Use open and onOpenChange props to control the open state of the alert dialog.
"use client";
import { useState } from "react";
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export function AlertDialogDemo() {
const [open, setOpen] = useState(false);
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger render={<Button />}>Save</AlertDialogTrigger>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure you want to save changes?</AlertDialogTitle>
<AlertDialogDescription>
Your changes will be saved and applied immediately. You can undo this action later if
needed.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
<AlertDialogClose render={<Button />}>Confirm</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
);
}With close confirmation
Enter some text and try to close the dialog to see a confirmation alert dialog before closing.
Note: Useful when you want to prevent users from losing unsaved changes.
"use client";
import { useState } from "react";
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogBody,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Field } from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
export function AlertDialogDemo() {
const [dialogOpen, setDialogOpen] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
const [value, setValue] = useState("");
return (
<Dialog
onOpenChange={(o) => {
if (!o && value) {
setConfirmOpen(true);
} else {
setDialogOpen(o);
}
}}
open={dialogOpen}
>
<DialogTrigger render={<Button variant="outline" />}>Compose</DialogTrigger>
<DialogPopup showCloseButton={false}>
<DialogHeader>
<DialogTitle>New message</DialogTitle>
<DialogDescription>Type something and try closing.</DialogDescription>
</DialogHeader>
<Form
className="contents"
onSubmit={(event) => {
event.preventDefault();
// Close the dialog when submitting
setDialogOpen(false);
}}
>
<DialogBody>
<Field>
<Textarea onChange={(e) => setValue(e.target.value)} value={value} />
</Field>
</DialogBody>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
<Button
onClick={() => {
setValue("");
setDialogOpen(false);
}}
>
Send
</Button>
</DialogFooter>
</Form>
</DialogPopup>
{/* Confirmation dialog */}
<AlertDialog onOpenChange={setConfirmOpen} open={confirmOpen}>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Discard changes?</AlertDialogTitle>
<AlertDialogDescription>Your message will be lost.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogClose render={<Button variant="ghost" />}>Go back</AlertDialogClose>
<Button
onClick={() => {
setConfirmOpen(false);
setValue("");
setDialogOpen(false);
}}
>
Discard
</Button>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
</Dialog>
);
}Backdrop variants
The backdrop of the alert dialog can be customized using the backdropVariant prop on AlertDialogPopup. The available variants are blur (default), opaque, and transparent.
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
const backdropVariants: React.ComponentProps<typeof AlertDialogPopup>["backdropVariant"][] = [
"blur",
"opaque",
"transparent",
];
export function AlertDialogDemo() {
return (
<div className="flex flex-wrap items-center gap-5">
{backdropVariants.map((variant) => (
<AlertDialog key={variant}>
<AlertDialogTrigger render={<Button className="capitalize" />}>
{variant}
</AlertDialogTrigger>
<AlertDialogPopup backdropVariant={variant}>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove
your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter variant="muted">
<AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
<AlertDialogClose render={<Button variant="danger" />}>
Delete Account
</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
))}
</div>
);
}Popup position
Change the position of the popup by passing a position prop to AlertDialogPopup. The default position is center. Other options include top, bottom, top-left, top-right, bottom-left, bottom-right, top-center, and bottom-center.
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
const positions: React.ComponentProps<typeof AlertDialogPopup>["position"][] = [
"top-left",
"top",
"top-right",
"top-center",
"center",
"bottom-center",
"bottom-left",
"bottom",
"bottom-right",
];
export function AlertDialogDemo() {
return (
<div className="grid grid-cols-2 items-center gap-5 sm:grid-cols-3">
{positions.map((position) => (
<AlertDialog key={position}>
<AlertDialogTrigger render={<Button className="capitalize" />}>
{position}
</AlertDialogTrigger>
<AlertDialogPopup position={position}>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account and remove
your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter variant="muted">
<AlertDialogClose render={<Button variant="outline" />}>Cancel</AlertDialogClose>
<AlertDialogClose render={<Button variant="danger" />}>
Delete Account
</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
))}
</div>
);
}API Reference
Alert Dialog Backdrop props
Type
'blur' | 'opaque' | 'transparent'Default
'blur'Alert Dialog Viewport props
Type
'center' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center'Default
'center'Alert Dialog Popup props
Type
booleanDefault
falseType
booleanDefault
falseType
'blur' | 'opaque' | 'transparent'Default
'blur'Type
'center' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center'Default
'center'Alert Dialog Footer props
Type
'default' | 'muted'Default
'default'