Skip to content
Highlight

ComponentsUpdated Sep 9, 2026

Alert Dialog

A dialog that demands a decision. Same parts as Dialog, but it cannot be dismissed by clicking outside, and it uses the alertdialog role.

Import

Source: packages/ui/src/components/alert-dialog

tsx
import { AlertDialog } from '@highlight/ui'

Basic

Cancel first, destructive action last.

tsx
import { AlertDialog } from '@highlight/ui'

export default function AlertDialogBasic() {
  return (
    <AlertDialog>
      <AlertDialog.Trigger variant="danger">Delete account</AlertDialog.Trigger>
      <AlertDialog.Content>
        <AlertDialog.Header>
          <AlertDialog.Title>Delete your account?</AlertDialog.Title>
          <AlertDialog.Description>
            This cannot be undone. Your meetings, notes, and integrations are permanently removed.
          </AlertDialog.Description>
        </AlertDialog.Header>
        <AlertDialog.Footer>
          <AlertDialog.Close variant="secondary">Cancel</AlertDialog.Close>
          <AlertDialog.Close variant="danger">Delete account</AlertDialog.Close>
        </AlertDialog.Footer>
      </AlertDialog.Content>
    </AlertDialog>
  )
}

Guarding unsaved changes

Intercept onOpenChange on a Dialog and raise an AlertDialog instead of closing.

tsx
import { useState } from 'react'
import { AlertDialog, Button, Dialog } from '@highlight/ui'

export default function AlertDialogConfirmClose() {
  const [dialogOpen, setDialogOpen] = useState(false)
  const [confirmOpen, setConfirmOpen] = useState(false)
  const [draft, setDraft] = useState('')

  return (
    <>
      <Dialog
        open={dialogOpen}
        onOpenChange={(open) => {
          if (!open && draft) {
            setConfirmOpen(true)
            return
          }
          setDraft('')
          setDialogOpen(open)
        }}
      >
        <Dialog.Trigger>Compose note</Dialog.Trigger>
        <Dialog.Content>
          <Dialog.Header>
            <Dialog.Title>New note</Dialog.Title>
            <Dialog.Description>Closing with unsaved text asks first.</Dialog.Description>
          </Dialog.Header>
          <Dialog.Body>
            <textarea
              value={draft}
              onChange={(event) => setDraft(event.target.value)}
              placeholder="Type something, then close…"
              className="border-border/20 bg-background text-foreground focus-visible:border-border/40 min-h-28 w-full rounded-lg border px-3 py-2 text-sm outline-none"
            />
          </Dialog.Body>
        </Dialog.Content>
      </Dialog>

      <AlertDialog open={confirmOpen} onOpenChange={setConfirmOpen}>
        <AlertDialog.Content>
          <AlertDialog.Header>
            <AlertDialog.Title>Discard note?</AlertDialog.Title>
            <AlertDialog.Description>Your text will be lost.</AlertDialog.Description>
          </AlertDialog.Header>
          <AlertDialog.Footer>
            <AlertDialog.Close>Keep editing</AlertDialog.Close>
            <Button
              variant="danger"
              onClick={() => {
                setConfirmOpen(false)
                setDraft('')
                setDialogOpen(false)
              }}
            >
              Discard
            </Button>
          </AlertDialog.Footer>
        </AlertDialog.Content>
      </AlertDialog>
    </>
  )
}

Parts

Compound sub-components. Every part accepts className and forwards the rest of its props to Base UI.

AlertDialog.Trigger / Close
Buttons; take variant, size, shape.
AlertDialog.Content
Portal + Backdrop + Popup. showClose defaults to true.
AlertDialog.Header / Body / Footer / Title / Description
Same as Dialog.