An alert dialog is a modal dialog that interrupts the user with important content and expects a response. It should be used sparingly for critical confirmations or warnings that require explicit user acknowledgment.
Installation
npx shadcn@latest add @eo-n/alert-dialog
Install dependencies
npm install @base-ui/react
Copy component code
Copy and paste the following code into components/ui/alert-dialog.tsx:"use client";
import * as React from "react";
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react";
import { cn } from "@/lib/utils";
import { buttonVariants } from "@/components/ui/button";
const AlertDialog = AlertDialogPrimitive.Root;
function AlertDialogTrigger({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
);
}
function AlertDialogPortal({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
);
}
function AlertDialogBackdrop({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Backdrop>) {
return (
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-backdrop"
className={cn(
"fixed inset-0 z-50 bg-black/50 backdrop-blur-[1.5px] transition-colors duration-150 ease-out data-[ending-style]:opacity-0 data-[starting-style]:opacity-0",
className
)}
{...props}
/>
);
}
function AlertDialogContent({
className,
children,
flush = false,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Popup> & {
flush?: boolean;
}) {
return (
<AlertDialogPortal>
<AlertDialogBackdrop />
<AlertDialogPrimitive.Viewport className="fixed inset-0 z-50 flex items-center justify-center p-4">
<AlertDialogPrimitive.Popup
data-slot="alert-dialog-content"
data-flush={flush}
className={cn(
"bg-background group fixed flex max-h-[calc(100%-2rem)] w-full max-w-[calc(100%-2rem)] flex-col gap-4 overflow-hidden rounded-lg border p-6 shadow-lg transition-all duration-150 ease-out outline-none sm:max-w-lg",
flush && "gap-0 p-0",
className
)}
{...props}
>
{children}
</AlertDialogPrimitive.Popup>
</AlertDialogPrimitive.Viewport>
</AlertDialogPortal>
);
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn(
"flex flex-col gap-2 text-center sm:text-left",
"group-data-[flush=true]:p-6",
className
)}
{...props}
/>
);
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
"group-data-[flush=true]:bg-muted/30 group-data-[flush=true]:border-t group-data-[flush=true]:p-6",
className
)}
{...props}
/>
);
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn("text-lg font-semibold", className)}
{...props}
/>
);
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function AlertDialogClose({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Close>) {
return (
<AlertDialogPrimitive.Close
data-slot="alert-dialog-close"
className={cn(buttonVariants({ variant: "outline" }), className)}
{...props}
/>
);
}
export {
AlertDialog,
AlertDialogTrigger,
AlertDialogPortal,
AlertDialogBackdrop,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogClose,
};
Update imports
Update the import paths to match your project setup.
Usage
Import all parts and piece them together:
import {
AlertDialog,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
<AlertDialog>
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Confirm Deletion</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this item? This action cannot be undone.
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogClose>Cancel</AlertDialogClose>
<Button>Delete</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Examples
Basic Alert Dialog
import {
AlertDialog,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export default function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">Delete Account</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Confirm Deletion</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this item? This action cannot be
undone.
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogClose>Cancel</AlertDialogClose>
<Button variant="destructive">Delete</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
With Confirmation Callback
Handle the confirmation action with a callback:
import * as React from "react";
import {
AlertDialog,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export default function AlertDialogConfirmation() {
const [open, setOpen] = React.useState(false);
const handleConfirm = () => {
// Perform the delete action
console.log("Item deleted");
setOpen(false);
};
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>
<Button variant="destructive">Delete</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<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>
<AlertDialogFooter>
<AlertDialogClose>Cancel</AlertDialogClose>
<Button variant="destructive" onClick={handleConfirm}>
Continue
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
Control the dialog state to open it from a dropdown menu:
import * as React from "react";
import {
AlertDialog,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
export default function AlertDialogFromMenu() {
const menuTriggerRef = React.useRef<HTMLButtonElement>(null);
const [alertDialogOpen, setAlertDialogOpen] = React.useState(false);
return (
<>
<DropdownMenu>
<DropdownMenuTrigger ref={menuTriggerRef} asChild>
<Button>Actions</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => setAlertDialogOpen(true)}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<AlertDialog open={alertDialogOpen} onOpenChange={setAlertDialogOpen}>
<AlertDialogContent finalFocus={menuTriggerRef}>
<AlertDialogTitle>Are you sure you want to proceed?</AlertDialogTitle>
<AlertDialogDescription>
This action may have permanent effects. Please confirm if you want
to continue.
</AlertDialogDescription>
</AlertDialogContent>
</AlertDialog>
</>
);
}
Make sure to use the dialog’s finalFocus prop to return focus back to the menu trigger for proper accessibility.
With Custom Actions
Customize the footer with multiple action buttons:
import {
AlertDialog,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export default function AlertDialogCustomActions() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button>Save Changes</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Unsaved Changes</AlertDialogTitle>
<AlertDialogDescription>
You have unsaved changes. Would you like to save them before leaving?
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogClose>Don't Save</AlertDialogClose>
<AlertDialogClose asChild>
<Button variant="outline">Cancel</Button>
</AlertDialogClose>
<Button>Save</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
API Reference
AlertDialog
The root component that manages the alert dialog state.
Controls the open state of the dialog.
The initial open state for uncontrolled usage.
Callback fired when the open state changes.
AlertDialogTrigger
The button that opens the alert dialog.
Merge props onto the child element instead of wrapping it.
AlertDialogContent
Contains the content to be rendered in the open alert dialog.
Removes padding and gap from the content container. Useful when using AlertDialogHeader and AlertDialogFooter with borders.
finalFocus
React.RefObject<HTMLElement>
Element to receive focus when the dialog closes.
Additional CSS classes to apply.
Wrapper for the alert dialog title and description.
Additional CSS classes to apply.
Wrapper for alert dialog action buttons.
Additional CSS classes to apply.
AlertDialogTitle
The accessible title of the alert dialog. Required for accessibility.
Additional CSS classes to apply.
AlertDialogDescription
The accessible description of the alert dialog.
Additional CSS classes to apply.
AlertDialogClose
A button that closes the alert dialog. Automatically styled as an outline button.
Merge props onto the child element instead of wrapping it.
Additional CSS classes to apply.