Installation
pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/alert-dialog.jsonInstalls to @/components/ui/alert-dialog.
Manual installation
Install the dependencies:
pnpm add @base-ui/react@^1.8.0Add the components it builds on: button.
Copy the source into your project:
'use client'
import { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog'
import * as React from 'react'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
}
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
}
function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) {
return (
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-overlay"
className={cn(
'fixed inset-0 isolate z-50 bg-black/50 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
className,
)}
{...props}
/>
)
}
function AlertDialogContent({
className,
size = 'default',
...props
}: AlertDialogPrimitive.Popup.Props & {
size?: 'default' | 'sm'
}) {
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Popup
data-slot="alert-dialog-content"
data-size={size}
className={cn(
'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 max-h-[calc(100dvh-2rem)] gap-4 overflow-y-auto rounded-xl bg-background p-4 text-foreground ring-1 ring-border duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
className,
)}
{...props}
/>
</AlertDialogPortal>
)
}
function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="alert-dialog-header"
className={cn(
'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
className,
)}
{...props}
/>
)
}
function AlertDialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
'-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
className,
)}
{...props}
/>
)
}
function AlertDialogMedia({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="alert-dialog-media"
className={cn(
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
className,
)}
{...props}
/>
)
}
function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn(
'font-heading text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
className,
)}
{...props}
/>
)
}
function AlertDialogDescription({ className, ...props }: AlertDialogPrimitive.Description.Props) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn(
'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
className,
)}
{...props}
/>
)
}
function AlertDialogAction({
className,
variant = 'default',
size = 'default',
...props
}: AlertDialogPrimitive.Close.Props & Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
return (
<AlertDialogPrimitive.Close
data-slot="alert-dialog-action"
className={cn(className)}
render={<Button variant={variant} size={size} />}
{...props}
/>
)
}
function AlertDialogCancel({
className,
variant = 'outline',
size = 'default',
...props
}: AlertDialogPrimitive.Close.Props & Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
return (
<AlertDialogPrimitive.Close
data-slot="alert-dialog-cancel"
className={cn(className)}
render={<Button variant={variant} size={size} />}
{...props}
/>
)
}
export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger,
}A modal dialog component that interrupts the user with important content and expects a response. Built on top of Base UI Alert Dialog, it follows WAI-ARIA design patterns and provides a fully accessible dialog experience.
Features
- Focus management
- Keyboard navigation
- Screen reader announcements
- Responsive design
- Customizable content
- Animated transitions
- Blurred backdrop overlay
- Two sizes:
defaultandsm(sizeonAlertDialogContent) - Optional icon/media slot (
AlertDialogMedia) AlertDialogActionandAlertDialogCancelrender asButton, accept itsvariantandsize, and close the dialog when clicked- Render the trigger as any component with the
renderprop, e.g.render={<Button />} - Mobile-friendly
- ARIA compliant
Usage
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
AlertDialogMedia,
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import { Trash2Icon } from 'lucide-react'
// Basic usage
export function WithdrawDialog() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>Withdraw application</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Withdraw case 2026-04812?</AlertDialogTitle>
<AlertDialogDescription>
The application will be closed and the handling officer notified.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Keep application</AlertDialogCancel>
<AlertDialogAction variant="destructive">Withdraw</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}
// Small size with media
export function DeleteDraftDialog() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="destructive" />}>Delete draft</AlertDialogTrigger>
<AlertDialogContent size="sm">
<AlertDialogHeader>
<AlertDialogMedia>
<Trash2Icon />
</AlertDialogMedia>
<AlertDialogTitle>Delete draft?</AlertDialogTitle>
<AlertDialogDescription>Your draft application will be deleted permanently.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction variant="destructive">Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Examples
Default
Confirm before a resident sends a school placement application that can’t be changed afterwards.
Destructive
A destructive action (variant="destructive" on AlertDialogAction) for withdrawing a building permit application.
Name the action on the button, not just "OK".
Small
A compact dialog (size="sm") with centered content and full-width footer buttons.
With Media
An icon in AlertDialogMedia sits beside the title on larger screens. Tint it with className.
Small with Media
At size="sm" the media is centered above the title.
With Custom Content
Put a summary between the header and the footer so the user can check what they are cancelling.
With Form
A required message before a case is returned to the applicant. The action stays disabled until the field is filled in.
Props
AlertDialogContent
size-'default' | 'sm'(default'default')className- Additional CSS classes
AlertDialogMedia
className- Additional CSS classeschildren- Icon or image shown above (or, at default size on larger screens, beside) the title
AlertDialogAction
variant- Button variant (default'default')size- Button size (default'default')- Closes the dialog when clicked. For async work, control the dialog with
open/onOpenChangeand use a plainButtoninstead.
AlertDialogCancel
variant- Button variant (default'outline')size- Button size (default'default')
Accessibility
The Alert Dialog component follows WAI-ARIA guidelines for modal dialogs:
- Uses
role="alertdialog"for critical interactions - Manages focus trap within dialog
- Supports keyboard navigation:
Esc: Close dialogTab: Navigate focusable elementsSpace/Enter: Activate buttons
- Announces dialog title and description
- Provides clear action buttons
- Maintains focus on trigger after closing
Best Practices
-
Content Guidelines:
- Use clear, concise language
- Explain consequences of actions
- Provide obvious choices
- Keep content focused
-
Visual Design:
- Use appropriate spacing
- Maintain clear hierarchy
- Highlight destructive actions
- Ensure readable text
-
Interaction:
- Provide clear feedback
- Allow easy dismissal
- Handle keyboard input
- Consider mobile touch targets
-
Accessibility:
- Write descriptive labels
- Use semantic markup
- Test with screen readers
- Verify keyboard navigation
-
Implementation:
- Handle state changes
- Manage focus correctly
- Consider animations
- Test edge cases
Related Components
- Dialog
- Sheet
- Popover
- Sheet