Home

Dialog

A card that opens over a dimmed page and holds one short task: a header, a panel that scrolls, and a footer of actions.

ui.dialogcomponents/ui/dialog.tsx

Use case

For the AI

Use it when a person must finish a small, focused task without leaving the page they are on: editing a client's billing details from the clients list, adding a line to an invoice, inviting a member with a role. The page waits behind the backdrop and comes back exactly as it was when the dialog closes. On a phone it sits at the foot of the screen (`bottomStickOnMobile` is on by default).

Use it when

  • A form of a few fields that edits or creates one record
  • A step the page must wait for, such as choosing a plan
  • Showing something once that must be read, such as a new API key
  • The task is short enough to finish in one sitting

Use something else when

  • The only question is whether to go ahead with something that cannot be undone: Alert dialog and confirm
  • Long content, filters for a list, or a record read beside the page: Sheet
  • A few words or one small control attached to a button, with no backdrop: Popover
  • A panel on a phone that the person swipes away: Drawer

Examples

Code

Import

import { useState } from "react"
import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogClose,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogPanel,
  DialogPopup,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

Usage

export function EditClient() {
  const [open, setOpen] = useState(false)
  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger render={<Button />}>Edit the Client</DialogTrigger>
      <DialogPopup className="sm:max-w-md">
        <form onSubmit={(e) => (e.preventDefault(), setOpen(false))}>
          <DialogHeader>
            <DialogTitle>Edit the client</DialogTitle>
            <DialogDescription>What goes on their invoices and their portal.</DialogDescription>
          </DialogHeader>
          <DialogPanel className="grid gap-4">
            <Field>
              <FieldLabel>Company name</FieldLabel>
              <Input name="company" defaultValue="Bistro Le Marchand" />
            </Field>
          </DialogPanel>
          <DialogFooter>
            <DialogClose render={<Button variant="ghost" type="button" />}>Cancel</DialogClose>
            <Button type="submit">Save Changes</Button>
          </DialogFooter>
        </form>
      </DialogPopup>
    </Dialog>
  )
}

Rules

  • A `<form>` placed directly inside `DialogPopup` wraps the header, panel and footer; it is laid out as contents, so the footer stays in the card and the panel scrolls between the bands.
  • The footer holds the actions: Cancel first (`variant="ghost"`, with `type="button"` inside a form), the primary last, both in Title Case.
  • The title says the task in sentence case and the description says what it changes in one line.
  • Content that can grow goes in `DialogPanel`, which scrolls and fades at its edges; never a bare `overflow-y-auto` inside the dialog.
  • Triggers and closes take their button through `render={<Button />}`, never a button nested in a button.