Alert Dialog
Usage
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger
} from '@jamie/ui/alert-dialog'
import { Button } from '@jamie/ui/button'
export function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
Share Transcript
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Share meeting transcript?</AlertDialogTitle>
<AlertDialogDescription>
This will share the full transcript of "Q3 Revenue Review" with all meeting
participants. They will receive an email notification.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Share</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Destructive
Use a destructive action button for irreversible operations. Combine with AlertDialogMedia to add a visual icon.
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
AlertDialogTrigger
} from '@jamie/ui/alert-dialog'
import { Button } from '@jamie/ui/button'
import { Trash2Icon } from 'lucide-react'
export function AlertDialogDestructiveDemo() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="destructive" />}>
Delete Meeting
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogMedia>
<Trash2Icon />
</AlertDialogMedia>
<AlertDialogTitle>Delete this meeting?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete the recording, transcript, and all AI-generated notes for
"Weekly Standup — Jan 15". This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction variant="destructive">Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Small Size
Use size="sm" on AlertDialogContent for a more compact dialog layout.
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger
} from '@jamie/ui/alert-dialog'
import { Button } from '@jamie/ui/button'
export function AlertDialogSmallDemo() {
return (
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>Stop Recording</AlertDialogTrigger>
<AlertDialogContent size="sm">
<AlertDialogHeader>
<AlertDialogTitle>Stop recording?</AlertDialogTitle>
<AlertDialogDescription>
Jamie will stop recording this meeting. You can restart it at any time.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Stop</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}Imperative API
Open an alert dialog from anywhere — no inline trigger, no provider, no hook. Use defineAlertDialog for confirmations triggered from menus, hotkeys, or async flows.
'use client'
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
defineAlertDialog
} from '@jamie/ui/alert-dialog'
import { Button } from '@jamie/ui/button'
import { toast } from '@jamie/ui/sonner'
const deleteMeetingDialog = defineAlertDialog<{ title: string; onConfirm: () => void }>(
({ title, onConfirm }) => (
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete “{title}”?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete the recording, transcript, and AI-generated notes for this
meeting. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => {
onConfirm()
deleteMeetingDialog.close()
}}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
)
)
export function AlertDialogImperativeDemo() {
return (
<>
<Button
variant="outline"
onClick={() =>
deleteMeetingDialog.open({
title: 'Q3 Revenue Review',
onConfirm: () => toast.success('Meeting deleted')
})
}
>
Delete Meeting
</Button>
<deleteMeetingDialog.Root />
</>
)
}Define the dialog
defineAlertDialog<Payload>(render) returns a { open, close, Root } object
Keep it module-scope so any caller can import it. The render function receives the typed
payloadfrom.open(payload).
import {
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
defineAlertDialog
} from '@jamie/ui/alert-dialog'
export const deleteMeetingDialog = defineAlertDialog<{
title: string
onConfirm: () => void
}>(({ title, onConfirm }) => (
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete “{title}”?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete the recording, transcript, and AI-generated notes for this
meeting. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
onClick={() => {
onConfirm()
deleteMeetingDialog.close()
}}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
))Mount the Root once
Render <deleteMeetingDialog.Root /> once, somewhere persistent — typically a top-level AppDialogs component.
export function AppDialogs() {
return <deleteMeetingDialog.Root />
}The Root portals to <body> and lazy-mounts its content only when the dialog opens.
Open from anywhere
Import the dialog and call .open(payload). Payload is typed.
import { toast } from '@jamie/ui/sonner'
deleteMeetingDialog.open({
title: 'Q3 Revenue Review',
onConfirm: () => {
deleteMeeting()
toast.success('Meeting deleted')
}
})