rapidUI

Alert Dialog

Modal confirmation dialog that interrupts the user and expects a response.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/alert-dialog.json

Installs to @/components/ui/alert-dialog.

Manual installation

Install the dependencies:

pnpm add @base-ui/react@^1.8.0

Add the components it builds on: button.

Copy the source into your project:

components/ui/alert-dialog.tsx
'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: default and sm (size on AlertDialogContent)
  • Optional icon/media slot (AlertDialogMedia)
  • AlertDialogAction and AlertDialogCancel render as Button, accept its variant and size, and close the dialog when clicked
  • Render the trigger as any component with the render prop, 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 classes
  • children - 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 / onOpenChange and use a plain Button instead.

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 dialog
    • Tab: Navigate focusable elements
    • Space/Enter: Activate buttons
  • Announces dialog title and description
  • Provides clear action buttons
  • Maintains focus on trigger after closing

Best Practices

  1. Content Guidelines:

    • Use clear, concise language
    • Explain consequences of actions
    • Provide obvious choices
    • Keep content focused
  2. Visual Design:

    • Use appropriate spacing
    • Maintain clear hierarchy
    • Highlight destructive actions
    • Ensure readable text
  3. Interaction:

    • Provide clear feedback
    • Allow easy dismissal
    • Handle keyboard input
    • Consider mobile touch targets
  4. Accessibility:

    • Write descriptive labels
    • Use semantic markup
    • Test with screen readers
    • Verify keyboard navigation
  5. Implementation:

    • Handle state changes
    • Manage focus correctly
    • Consider animations
    • Test edge cases
  • Dialog
  • Sheet
  • Popover
  • Sheet