Shadcn Alert Dialog for React and Tailwind
A modal for critical confirmations and destructive actions.
Alert Dialog is built on top of the Dialog component. Check it out for more examples.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/alert-dialog.jsonpnpm dlx shadcn@latest add https://kit.dev/r/alert-dialog.jsonnpx shadcn@latest add https://kit.dev/r/alert-dialog.jsonyarn shadcn@latest add https://kit.dev/r/alert-dialog.json<Step>This component depends on Button and Dialog. Install them first if you haven't already.</Step>
Install the following dependencies:
bun add @ark-ui/react lucide-react tailwind-variantspnpm add @ark-ui/react lucide-react tailwind-variantsnpm install @ark-ui/react lucide-react tailwind-variantsyarn add @ark-ui/react lucide-react tailwind-variantsCopy and paste the following code into your project.
"use client";
import type React from "react";
import { cn } from "@/lib/utils";
import { Button, type ButtonProps } from "@/components/ui/button";
import {
Dialog,
DialogBody,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
export const AlertDialog = (props: React.ComponentProps<typeof Dialog>) => (
<Dialog
closeOnInteractOutside={false}
data-slot="alert-dialog-root"
role="alertdialog"
{...props}
/>
);
export const AlertDialogTrigger = (
props: React.ComponentProps<typeof DialogTrigger>
) => <DialogTrigger data-slot="alert-dialog-trigger" {...props} />;
export const AlertDialogContent = (
props: React.ComponentProps<typeof DialogContent>
) => (
<DialogContent
data-slot="alert-dialog-content"
showCloseButton={false}
{...props}
/>
);
export const AlertDialogBody = (
props: React.ComponentProps<typeof DialogBody>
) => {
const { className, ...rest } = props;
return (
<DialogBody
className={cn(
"in-[[data-slot=alert-dialog-content]:has([data-slot=alert-dialog-header])]:pt-0",
className
)}
data-slot="alert-dialog-body"
{...rest}
/>
);
};
export const AlertDialogHeader = (
props: React.ComponentProps<typeof DialogHeader>
) => <DialogHeader data-slot="alert-dialog-header" {...props} />;
export const AlertDialogTitle = (
props: React.ComponentProps<typeof DialogTitle>
) => <DialogTitle data-slot="alert-dialog-title" {...props} />;
export const AlertDialogDescription = (
props: React.ComponentProps<typeof DialogDescription>
) => <DialogDescription data-slot="alert-dialog-description" {...props} />;
export const AlertDialogClose = (
props: React.ComponentProps<typeof DialogClose>
) => <DialogClose data-slot="alert-dialog-close" {...props} />;
export const AlertDialogFooter = (
props: React.ComponentProps<typeof DialogFooter>
) => <DialogFooter data-slot="alert-dialog-footer" {...props} />;
interface AlertDialogActionProps
extends React.ComponentProps<typeof DialogClose>,
Omit<ButtonProps, "variant"> {
/**
* The variant of the action button
*
* @default "default"
*/
variant?: "default" | "destructive";
}
export const AlertDialogAction = (props: AlertDialogActionProps) => {
const { variant = "default", ...rest } = props;
return <Button variant={variant} {...rest} />;
};
interface AlertDialogCancelProps
extends React.ComponentProps<typeof DialogClose>,
Omit<ButtonProps, "variant"> {}
export const AlertDialogCancel = (props: AlertDialogCancelProps) => (
<AlertDialogClose asChild data-slot="alert-dialog-cancel">
<Button variant="outline" {...props} />
</AlertDialogClose>
);Update the import paths to match your project setup.
Anatomy
AlertDialog
├── AlertDialogTrigger
└── AlertDialogContent
├── AlertDialogHeader
│ ├── AlertDialogTitle
│ └── AlertDialogDescription
├── AlertDialogBody
├── AlertDialogFooter
│ ├── AlertDialogCancel
│ └── AlertDialogAction
└── AlertDialogCloseUsage
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogBody,
AlertDialogFooter,
AlertDialogAction,
AlertDialogCancel,
} from "@/components/ui/alert-dialog";<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline">Open</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader
title="Allow accessory to connect?"
description="Do you want to allow the USB accessory to connect to this device?"
/>
<AlertDialogFooter>
<AlertDialogCancel>Don't allow</AlertDialogCancel>
<AlertDialogClose asChild>
<AlertDialogAction>Allow</AlertDialogAction>
</AlertDialogClose>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>Alert Dialog is Dialog with role="alertdialog". Differences:
- Focus moves to a safe action (typically Cancel) when possible
- Does not close on outside click (
closeOnInteractOutside={false}) - Must be dismissed with a button or Escape
- The corner close button is hidden (
showCloseButton={false})
Title & Description
AlertDialogHeader supports two usage patterns:
Using props
Pass title and description props directly to AlertDialogHeader.
<AlertDialogHeader
title="Allow accessory to connect?"
description="Do you want to allow the USB accessory to connect to this device?"
>
<AlertDialogAction />
</AlertDialogHeader>This approach does not require
AlertDialogTitleorAlertDialogDescriptioncomponents.
Using components
Use AlertDialogTitle and AlertDialogDescription as children for more control.
<AlertDialogHeader>
<AlertDialogTitle>Allow accessory to connect?</AlertDialogTitle>
<AlertDialogDescription>
Do you want to allow the USB accessory to connect to this device?
</AlertDialogDescription>
<AlertDialogAction />
</AlertDialogHeader>Examples
Destructive
API Reference
AlertDialog accepts every Dialog root prop. Defaults that differ from Dialog:
| Prop | Alert Dialog default | Dialog default |
|---|---|---|
role | "alertdialog" | "dialog" |
closeOnInteractOutside | false | true |
asChild merges props onto a single child element.
AlertDialog
Root. Same props as Dialog.
| Attribute | Description |
|---|---|
data-slot | alert-dialog-root |
role | alertdialog |
AlertDialogTrigger
Same as DialogTrigger.
| Attribute | Description |
|---|---|
data-slot | alert-dialog-trigger |
AlertDialogContent
Same as DialogContent, with showCloseButton={false} by default.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "fullscreen" | "md" | Max width of the panel. |
showCloseButton | boolean | false | Show the top-end close button. |
bottomStickOnMobile | boolean | true | Stick to the bottom of the viewport below sm. |
positionerClassName | string | - | Class names on the positioner. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the panel. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-content |
AlertDialogHeader
Same as DialogHeader.
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | Renders AlertDialogTitle. |
description | string | - | Renders AlertDialogDescription. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the header. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-header |
AlertDialogTitle
Same as DialogTitle. Required for accessibility.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the title. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-title |
AlertDialogDescription
Same as DialogDescription.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the description. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-description |
AlertDialogBody
Same as DialogBody.
| Prop | Type | Default | Description |
|---|---|---|---|
scrollFade | boolean | false | Fade the edges of the scroll area. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the body. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-body |
AlertDialogFooter
Same as DialogFooter.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the footer. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-footer |
AlertDialogClose
Same as DialogClose.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the close control. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-close |
AlertDialogCancel
Cancel action. Renders AlertDialogClose around an outline Button. Put this first so it is the safer focused control.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the button. |
children | ReactNode | - | Button label. |
| Attribute | Description |
|---|---|
data-slot | alert-dialog-cancel |
AlertDialogAction
Primary action. Renders a Button. Wrap it in AlertDialogClose when the action should also dismiss the dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "destructive" | "default" | Button variant. Use "destructive" for delete/remove. |
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the button. |
children | ReactNode | - | Button label. |
Accessibility
Complies with the Alert Dialog WAI-ARIA pattern. Always include a title. Prefer AlertDialogCancel as the first footer action.
Keyboard support
| Key | Description |
|---|---|
Enter | When focus is on the trigger, opens the dialog. |
Tab | Move to the next focusable element. Focus is trapped. |
Shift + Tab | Move to the previous focusable element. Focus is trapped. |
Escape | Close the dialog and return focus to the trigger. |