Dialog

A hand-drawn Base UI dialog styled with shadcn conventions. It renders in a portal, traps focus, and closes on outside click or Escape.

Dialog
Every hand-drawn dialog composition.
Basic
With footer actions
Destructive confirm
Without close button

Installation

npx shadcn@latest add https://sketchcn.chuwii.com/r/dialog.json

Usage

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";

export function DeleteDialog() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="destructive">Delete</Button>} />
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete this sketch?</DialogTitle>
          <DialogDescription>This cannot be undone.</DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost">Cancel</Button>} />
          <Button variant="destructive">Delete</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Parts

PartDescription
DialogRoot that owns the open state.
DialogTriggerOpens the dialog.
DialogContentThe centred, outlined popup.
DialogHeaderTitle and description block.
DialogTitleAccessible name of the dialog.
DialogDescriptionAccessible description.
DialogFooterAction row, stacked on small screens.
DialogCloseCloses the dialog.
DialogOverlayThe backdrop, rendered for you by DialogContent.
DialogPortalThe portal, also rendered for you.

Props

PropTypeDefaultDescription
showCloseButtonbooleantrueOn DialogContent. Hides the corner close button when false.

All other props are forwarded to the matching Base UI Dialog parts.

Examples

Form in a dialog

import { Button } from "@/components/ui/button";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";

export function NewSketchDialog() {
  return (
    <Dialog>
      <DialogTrigger render={<Button>New sketch</Button>} />
      <DialogContent>
        <DialogHeader>
          <DialogTitle>New sketch</DialogTitle>
          <DialogDescription>Give it a name before you start.</DialogDescription>
        </DialogHeader>
        <div className="flex flex-col gap-3">
          <Input placeholder="Sketch name" />
          <Textarea placeholder="What is it for?" />
        </div>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
          <Button>Create</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Controlled open state

import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";

export function UnsavedChangesDialog() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        Leave page
      </Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Leave without saving?</DialogTitle>
            <DialogDescription>Your sketch has unsaved changes.</DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => setOpen(false)}>
              Keep editing
            </Button>
            <Button variant="destructive" onClick={() => setOpen(false)}>
              Discard
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}