Skip to content
Corsair UI
Browse the docs

ComponentsNo. 09 of 148

Alert Dialog

Modal that asks the user to confirm or cancel before an action, with action and cancel buttons.

Installation

pnpm dlx shadcn@latest add @corsair-ui/alert-dialog

No setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.

Also adds
utilsbutton
npm
@radix-ui/react-alert-dialog@radix-ui/react-slotclass-variance-authorityclsxlucide-reacttailwind-merge

Examples

Confirm a destructive action

Focus starts on Cancel, and a click outside does not close it.

API

PropTypeDefault
AlertDialogAction / AlertDialogCancel

Styled as the default and outline buttons; both close it.

button—
overlayClassName

AlertDialogContent: classes for the overlay behind it.

string—

Built on Radix Alert Dialog; every prop it takes is passed through.

Styling

Restyle it in your project: className on each part, the theme variables, and these attributes with arbitrary variants, e.g. className="[&_[data-slot=alert-dialog-action]]:text-primary".

  • overlayClassName on AlertDialogContent styles the overlay behind it.
data-slot
  • alert-dialog
  • alert-dialog-action
  • alert-dialog-cancel
  • alert-dialog-content
  • alert-dialog-description
  • alert-dialog-footer
  • alert-dialog-header
  • alert-dialog-overlay
  • alert-dialog-portal
  • alert-dialog-title
  • alert-dialog-trigger

Radix Alert Dialog also sets its own attributes on the parts, such as data-state and data-disabled.

Accessibility

  • role="alertdialog": for decisions that need an answer.
  • Focus starts on Cancel, the safe choice. A click outside does not close it; Escape does.

Compatibility

Tailwind CSS
3.4 and 4, both checked in CI
React
19
Rendering
Client component ("use client")

Source

1 file, installed as source you own
alert-dialog.tsxView on GitHub
"use client";
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
import type { ComponentProps } from "react";
import { buttonVariants } from "@/registry/default/ui/button";
import { cn } from "@/registry/default/lib/utils";

/**
 * A modal that asks the user to confirm or cancel before something happens,
 * such as deleting data. Unlike a dialog, a click on the overlay does not
 * close it: the user has to pick an answer or press Escape, which cancels.
 * Focus starts on the cancel button, stays trapped inside and returns to the
 * trigger afterwards. Screen readers announce it as an alert dialog named by
 * `AlertDialogTitle` and described by `AlertDialogDescription`. With reduced
 * motion it appears without fading or zooming.
 *
 * @example
 * <AlertDialog>
 *   <AlertDialogTrigger asChild>
 *     <Button variant="destructive">Delete</Button>
 *   </AlertDialogTrigger>
 *   <AlertDialogContent>
 *     <AlertDialogHeader>
 *       <AlertDialogTitle>Delete this file?</AlertDialogTitle>
 *       <AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
 *     </AlertDialogHeader>
 *     <AlertDialogFooter>
 *       <AlertDialogCancel>Cancel</AlertDialogCancel>
 *       <AlertDialogAction onClick={remove}>Delete</AlertDialogAction>
 *     </AlertDialogFooter>
 *   </AlertDialogContent>
 * </AlertDialog>
 */
function AlertDialog(props: ComponentProps<typeof AlertDialogPrimitive.Root>) {
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
}

function AlertDialogTrigger(props: ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
  return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
}

function AlertDialogPortal(props: ComponentProps<typeof AlertDialogPrimitive.Portal>) {
  return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
}

function AlertDialogOverlay({
  className,
  ...props
}: ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
  return (
    <AlertDialogPrimitive.Overlay
      data-slot="alert-dialog-overlay"
      className={cn(
        "fixed inset-0 z-50 bg-black/50",
        // animate-in/out sit behind motion-safe: so reduced motion really turns them off.
        "motion-safe:data-[state=open]:animate-in data-[state=open]:fade-in-0",
        "motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
        className
      )}
      {...props}
    />
  );
}

interface AlertDialogContentProps extends ComponentProps<typeof AlertDialogPrimitive.Content> {
  /**
   * Classes for the overlay behind the window: opacity, blur, z-index. The
   * overlay is also `data-slot="alert-dialog-overlay"` for styling from outside.
   */
  overlayClassName?: string;
}

/** The window itself, rendered in a portal above an overlay. It needs an `AlertDialogTitle`. */
function AlertDialogContent({ className, overlayClassName, ...props }: AlertDialogContentProps) {
  return (
    <AlertDialogPortal>
      <AlertDialogOverlay className={overlayClassName} />
      <AlertDialogPrimitive.Content
        data-slot="alert-dialog-content"
        className={cn(
          "bg-background fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg outline-none sm:max-w-lg",
          "motion-safe:data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
          "motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
          className
        )}
        {...props}
      />
    </AlertDialogPortal>
  );
}

function AlertDialogHeader({ className, ...props }: ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-header"
      className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
      {...props}
    />
  );
}

function AlertDialogFooter({ className, ...props }: ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-footer"
      className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
      {...props}
    />
  );
}

/** Names the alert dialog for screen readers. */
function AlertDialogTitle({
  className,
  ...props
}: ComponentProps<typeof AlertDialogPrimitive.Title>) {
  return (
    <AlertDialogPrimitive.Title
      data-slot="alert-dialog-title"
      className={cn("text-lg leading-none font-semibold", className)}
      {...props}
    />
  );
}

/** Read out after the title: say what will happen if the user confirms. */
function AlertDialogDescription({
  className,
  ...props
}: ComponentProps<typeof AlertDialogPrimitive.Description>) {
  return (
    <AlertDialogPrimitive.Description
      data-slot="alert-dialog-description"
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  );
}

/** Confirms and closes. Styled as a primary button; pass `className` to restyle it. */
function AlertDialogAction({
  className,
  ...props
}: ComponentProps<typeof AlertDialogPrimitive.Action>) {
  return (
    <AlertDialogPrimitive.Action
      data-slot="alert-dialog-action"
      className={cn(buttonVariants(), className)}
      {...props}
    />
  );
}

/** Cancels and closes. Escape does the same. */
function AlertDialogCancel({
  className,
  ...props
}: ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
  return (
    <AlertDialogPrimitive.Cancel
      data-slot="alert-dialog-cancel"
      // "outline" is the button variant name, not the Tailwind utility.
      // tailwind-compat-ignore-next-line
      className={cn(buttonVariants({ variant: "outline" }), className)}
      {...props}
    />
  );
}

export {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogTitle,
  AlertDialogTrigger,
  type AlertDialogContentProps,
};