AlertDialog
A focus-trapping modal that interrupts the user to confirm an important or destructive action.
Uses role="alertdialog" aria-modal="true"; focus is trapped; cannot be dismissed by Esc or backdrop click — only via AlertDialogAction or AlertDialogCancel.
Preview
tsx
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
} from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="solid" colorScheme="danger">
Delete project
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete this project?</AlertDialogTitle>
<AlertDialogDescription>
This permanently removes the project and all associated data. You can not undo it.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel asChild>
<Button variant="ghost">Cancel</Button>
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button variant="solid" colorScheme="danger">
Delete
</Button>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
Variants
Section titled “Variants”Controlled
Section titled “Controlled”Drive the open state externally with open / onOpenChange to gate confirmation on async work or to coordinate with other UI.
Preview
tsx
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
} from '@arshad-shah/cynosure-react';
import { useState } from 'react';
export default function Example() {
const [open, setOpen] = useState(false);
const [pending, setPending] = useState(false);
const [done, setDone] = useState(false);
async function handleConfirm() {
setPending(true);
await new Promise((resolve) => setTimeout(resolve, 700));
setPending(false);
setOpen(false);
setDone(true);
}
return (
<div
style={{ display: 'flex', flexDirection: 'column', gap: '1rem', alignItems: 'flex-start' }}
>
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>
<Button variant="solid" colorScheme="danger">
Revoke API key
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Revoke this API key?</AlertDialogTitle>
<AlertDialogDescription>
Applications using this key will stop working immediately.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel asChild>
<Button variant="ghost" disabled={pending}>
Cancel
</Button>
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button
variant="solid"
colorScheme="danger"
disabled={pending}
onClick={(event) => {
event.preventDefault();
handleConfirm();
}}
>
{pending ? 'Revoking…' : 'Revoke'}
</Button>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
{done && <p style={{ fontSize: '0.875rem', margin: 0 }}>API key revoked.</p>}
</div>
);
}
AlertDialogContent accepts the same six sizes as Dialog: xs, sm (default), md, lg, xl, full. Keep confirmations small — there is rarely a need to go beyond sm.
Preview
tsx
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
} from '@arshad-shah/cynosure-react';
const sizes = ['xs', 'sm', 'md', 'lg'] as const;
export default function Example() {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem' }}>
{sizes.map((size) => (
<AlertDialog key={size}>
<AlertDialogTrigger asChild>
<Button variant="outline">{size}</Button>
</AlertDialogTrigger>
<AlertDialogContent size={size}>
<AlertDialogHeader>
<AlertDialogTitle>Size: {size}</AlertDialogTitle>
<AlertDialogDescription>
AlertDialogContent uses size="{size}" to set its max-width.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel asChild>
<Button variant="ghost">Cancel</Button>
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button>OK</Button>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
))}
</div>
);
}
Top position
Section titled “Top position”Pass position="top" to anchor the panel near the top of the viewport — useful on long pages where the user may have scrolled far from the trigger.
Preview
tsx
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
Button,
} from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline">Open at top</Button>
</AlertDialogTrigger>
<AlertDialogContent position="top">
<AlertDialogHeader>
<AlertDialogTitle>Pinned near the top</AlertDialogTitle>
<AlertDialogDescription>
Useful on long pages where the user may have scrolled far from the trigger.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel asChild>
<Button variant="ghost">Dismiss</Button>
</AlertDialogCancel>
<AlertDialogAction asChild>
<Button>Acknowledge</Button>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
PropTypeDefaultDescription
size
"xs"|"sm"|"md"|"lg"|"xl"|"full"
sm
position
"center"|"top"
center
container
HTMLElement|(() => HTMLElement)
—
hideOverlay
boolean
false
Accessibility
Section titled “Accessibility”AlertDialogContentrenders withrole="alertdialog"andaria-modal="true", sharing the same focus-trap + scroll-lock kit asDialog.- Focus is trapped inside the dialog while open and returned to the trigger on close.
- Backdrop clicks never dismiss;
Escis also suppressed — the user must hitAlertDialogActionorAlertDialogCancel. AlertDialogTitlemaps toaria-labelledby;AlertDialogDescriptionmaps toaria-describedby— both are required for a complete accessible name.- Default focus lands on the cancel action, making accidental confirmation unlikely.
Recipes
Section titled “Recipes”- Reserve
AlertDialogfor destructive or non-reversible choices. For everything else, useDialog. - Pair the destructive action with
colorScheme="danger"to reinforce the intent visually. - Keep the body copy short — one or two sentences naming the consequence.
- Use the controlled pattern when the action is async — disable the button and close on success.