Unified UI

Confirm Dialog

A pre-composed confirmation dialog built on AlertDialog primitives. Supports danger variant, loading state, and custom content slot.

Basic

A pre-composed confirmation dialog with title, description, confirm/cancel actions, danger variant, and loading state. Built on AlertDialog primitives — no overlay-click dismissal by default.

Installation

Install the component via the CLI in one command.

npx @work-rjkashyap/unified-ui add confirm-dialog
pnpm dlx @work-rjkashyap/unified-ui add confirm-dialog
npx @work-rjkashyap/unified-ui add confirm-dialog
bunx @work-rjkashyap/unified-ui add confirm-dialog

If you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for details.

Or install the full package

Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.

npm install @work-rjkashyap/unified-ui
pnpm add @work-rjkashyap/unified-ui
yarn add @work-rjkashyap/unified-ui
bun add @work-rjkashyap/unified-ui

Anatomy

import {
	ConfirmDialog,
	AlertDialog,
	AlertDialogTrigger,
	AlertDialogContent,
	AlertDialogHeader,
	AlertDialogTitle,
	AlertDialogDescription,
	AlertDialogFooter,
	AlertDialogAction,
	AlertDialogCancel,
	Button,
} from "@work-rjkashyap/unified-ui";

Basic Usage

ConfirmDialog is a pre-composed wrapper around the AlertDialog primitives. Pass a trigger, title, description, and action labels — everything else is handled for you.

<ConfirmDialog
	trigger={<Button variant="secondary">Archive Project</Button>}
	title="Archive this project?"
	description="The project will be hidden from your dashboard but can be restored from the settings page."
	confirmLabel="Archive"
	cancelLabel="Cancel"
	onConfirm={() => archiveProject(id)}
/>

Variants

Default

The default variant uses the primary action style for the confirm button — suitable for non-destructive confirmations like archiving, publishing, or submitting.

Danger

The danger variant applies a red background to the confirm button. Use it for destructive, irreversible actions like deletion.

Loading State

Set loading={true} to disable both action buttons and show a spinner on the confirm button. Use this while an async operation is in progress.

Controlled Mode

Use open and onOpenChange to control the dialog programmatically without a trigger element.

const [open, setOpen] = useState(false);

<>
	<Button variant="danger" onClick={() => setOpen(true)}>
		Delete All
	</Button>
	<ConfirmDialog
		open={open}
		onOpenChange={setOpen}
		variant="danger"
		title="Delete all records?"
		description={`You are about to delete ${selectedCount} records. This cannot be undone.`}
		confirmLabel="Delete All"
		cancelLabel="Cancel"
		onConfirm={async () => {
			await deleteAll();
			setOpen(false);
		}}
		onCancel={() => setOpen(false)}
	/>
</>;

Custom Content

Use the children prop to render additional content between the description and the action buttons — input fields, verification text, checklists, etc.

<ConfirmDialog
	trigger={<Button variant="danger">Delete Workspace</Button>}
	variant="danger"
	title="Delete workspace?"
	description="Type the workspace name to confirm deletion."
	confirmLabel="Delete Workspace"
	cancelLabel="Cancel"
>
	<div className="flex flex-col gap-1.5 mt-1">
		<label className="text-xs font-medium text-muted-foreground">
			Type <strong>my-workspace</strong> to confirm
		</label>
		<Input
			placeholder="my-workspace"
			value={confirmName}
			onChange={(e) => setConfirmName(e.target.value)}
		/>
	</div>
</ConfirmDialog>

Async Confirm Flow

A complete pattern with async mutation, loading state, and success feedback.

import { useState } from "react";
import { ConfirmDialog, Button, toast } from "@work-rjkashyap/unified-ui";

function DeleteProjectButton({ projectId }: { projectId: string }) {
	const [open, setOpen] = useState(false);
	const [loading, setLoading] = useState(false);

	const handleConfirm = async () => {
		setLoading(true);
		try {
			await deleteProject(projectId);
			setOpen(false);
			toast.success("Project deleted successfully.");
		} catch {
			toast.error("Failed to delete the project. Please try again.");
		} finally {
			setLoading(false);
		}
	};

	return (
		<ConfirmDialog
			open={open}
			onOpenChange={setOpen}
			trigger={
				<Button variant="danger" size="sm">
					Delete Project
				</Button>
			}
			variant="danger"
			title="Delete this project?"
			description="All repositories, environments, and deployment history will be permanently removed."
			confirmLabel="Delete Project"
			cancelLabel="Keep Project"
			loading={loading}
			onConfirm={handleConfirm}
			onCancel={() => setOpen(false)}
		/>
	);
}

Using AlertDialog Primitives Directly

For fully custom confirmation dialogs, compose AlertDialog primitives directly. AlertDialog differs from Dialog in that clicking the overlay backdrop does not close it — the user must explicitly choose an action.

<AlertDialog>
	<AlertDialogTrigger asChild>
		<Button variant="danger">Revoke API Key</Button>
	</AlertDialogTrigger>
	<AlertDialogContent>
		<AlertDialogHeader>
			<AlertDialogTitle>Revoke API key?</AlertDialogTitle>
			<AlertDialogDescription>
				Applications using this key will stop working immediately.
				You'll need to generate a new key and update your integrations.
			</AlertDialogDescription>
		</AlertDialogHeader>
		<div className="my-3 p-3 bg-muted rounded-md font-mono text-xs text-muted-foreground">
			api_xyz_xxxxxxxxxxxxxxxxxxxxxxxxxxx
		</div>
		<AlertDialogFooter>
			<AlertDialogCancel>Cancel</AlertDialogCancel>
			<AlertDialogAction
				className="bg-danger text-danger-foreground hover:bg-danger-hover"
				onClick={revokeKey}
			>
				Revoke Key
			</AlertDialogAction>
		</AlertDialogFooter>
	</AlertDialogContent>
</AlertDialog>

ConfirmDialog vs Dialog

FeatureConfirmDialog / AlertDialogDialog
Click overlay to dismissNoYes
Focus trapYesYes
Escape to dismissYes (configurable)Yes
Action-required UXYesNo
Pre-composed APIConfirmDialog onlyYes
Slot-based compositionVia AlertDialog primitivesYes

Use ConfirmDialog or AlertDialog when the user must make an active choice before proceeding. Use Dialog for informational or form modals that can be dismissed freely.

Props

ConfirmDialog

PropTypeDefaultDescription
openbooleanControlled open state.
onOpenChange(open: boolean) => voidCallback when open state changes.
triggerReactNodeElement that opens the dialog. Rendered inside AlertDialogTrigger asChild.
titleReactNode"Are you sure?"Dialog heading text.
descriptionReactNode"This action cannot be undone."Supporting text below the title.
confirmLabelstring"Confirm"Label for the confirm/action button.
cancelLabelstring"Cancel"Label for the cancel button.
variant"default" | "danger""default""danger" applies a red background to the confirm button.
onConfirm() => voidCallback fired when the confirm button is clicked.
onCancel() => voidCallback fired when the cancel button is clicked.
loadingbooleanfalseShows a spinner and disables both buttons.
classNamestringAdditional CSS classes on the AlertDialogContent element.
childrenReactNodeCustom content rendered between the description and footer actions.

AlertDialogContent

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

Accessibility

  • Built on @radix-ui/react-alert-dialog which implements the WAI-ARIA alertdialog role.
  • Focus is trapped inside the dialog while open.
  • Focus returns to the trigger element on close.
  • Pressing Escape closes the dialog (unless onEscapeKeyDown is overridden).
  • Clicking the overlay does not close the dialog — the user must take an explicit action.
  • AlertDialogTitle is linked to the dialog via aria-labelledby.
  • AlertDialogDescription is linked via aria-describedby.
  • Both AlertDialogAction and AlertDialogCancel are standard <button> elements with full keyboard support.
  • The loading spinner is aria-hidden="true" — the loading state disables buttons without removing them from the accessibility tree.

Design Tokens

TokenUsage
--backgroundDialog panel background
--borderDialog panel border
--overlayBackdrop overlay color
--primaryDefault confirm button background
--dangerDanger confirm button background
--secondaryCancel button background
--radius-lgDialog border radius
--z-modalZ-index for dialog content
--z-overlayZ-index for backdrop
--shadow-xlDialog panel shadow
--duration-fastButton transition speed

On this page