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.dialog
components/ui/dialog.tsxUse case
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.