Dialog

Modal window for confirmations and focused tasks.

Installation

npm install @aspyn-io/kit @aspyn-io/tokens
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
  DialogClose,
} from "@aspyn-io/kit";

Destructive confirmation

The standard confirm pattern for irreversible actions.

<Dialog>
  <DialogTrigger asChild>
    <Button variant="destructive">Delete workspace</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Delete workspace?</DialogTitle>
      <DialogDescription>
        This permanently deletes the workspace and all of its data.
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="ghost">Cancel</Button>
      </DialogClose>
      <Button variant="destructive">Delete</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Scrollable body

Long content? DialogBody pins the header and footer, scrolls the middle, and separates it with borders — no layout work required.

<DialogContent>
  <DialogHeader>
    <DialogTitle>Terms of service</DialogTitle>
    <DialogDescription>Updated August 2026.</DialogDescription>
  </DialogHeader>
  <DialogBody>
    {/* any amount of content — scrolls between pinned header/footer */}
  </DialogBody>
  <DialogFooter>
    <Button>Accept</Button>
  </DialogFooter>
</DialogContent>

Form in a dialog

<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
    </DialogHeader>
    <div className="space-y-2">
      <Label htmlFor="project-name">Project name</Label>
      <Input id="project-name" defaultValue="aspyn-crm" />
    </div>
    <DialogFooter>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Notes

  • Focus is trapped inside the dialog and returned to the trigger on close (Radix).
  • Escape and clicking the overlay close it; DialogTitle is required for accessible naming.
  • For controlled dialogs (e.g. close after async save), use open + onOpenChange.