Getting Started
Components
- Accordion
- Alert
- Alert Dialog
- Avatar
- Badge
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Circular Progress
- Collapsible
- Compare Slider
- Dialog
- Dropdown Menu
- Gauge
- Input
- Label
- Marquee
- Navigation Menu
- Number Field
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Tooltip
Demos
A dialog that holds a short form.
"use client"
import { Button } from "@workspace/ui/components/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@workspace/ui/components/dialog"
import { Input } from "@workspace/ui/components/input"
export function DialogDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Edit profile
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Reference ranges use your age and sex. Keep them current.
</DialogDescription>
</DialogHeader>
<div className="grid gap-3">
<label className="grid gap-1.5 text-xs font-medium">
Name
<Input defaultValue="Alex Morgan" />
</label>
<label className="grid gap-1.5 text-xs font-medium">
Date of birth
<Input type="date" defaultValue="1994-05-12" />
</label>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
<DialogClose render={<Button />}>Save</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Usage
Use a dialog for a short task or a decision that blocks the current flow: confirm a delete, edit a profile field. For longer forms or side content, use a Sheet. Do not open a dialog the reader did not ask for.
Import it from @workspace/ui/components/dialog.
Anatomy
- Dialog — Holds the open state.
- DialogTrigger — Opens the dialog.
- DialogContent — The window, with backdrop and a close button.
- DialogHeader — Stacks the title and description.
- DialogTitle — Names the dialog for screen readers. Always include it.
- DialogDescription — Supporting text.
- DialogFooter — Actions. Stacks on small screens.
- DialogClose — Closes the dialog. Pass a button through
render. - DialogPortal and DialogOverlay — The portal and backdrop. DialogContent already renders both, so you rarely need them.
Notes
- Focus moves into the dialog on open and back to the trigger on close.
- Escape and a click on the backdrop close the dialog.
- Put the safe action first in the footer and the destructive one last.
Examples
Confirm
Confirm a destructive action. showCloseButton={false} hides the X so the reader picks an answer.
"use client"
import { Button } from "@workspace/ui/components/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@workspace/ui/components/dialog"
export function DialogConfirm() {
return (
<Dialog>
<DialogTrigger render={<Button variant="destructive" />}>
Delete report
</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Delete this report?</DialogTitle>
<DialogDescription>
The October panel and its notes will be removed. This cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Keep report</DialogClose>
<DialogClose render={<Button variant="destructive" />}>Delete</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Controlled
Hold the state with open and onOpenChange to close the dialog after an action finishes.
"use client"
import * as React from "react"
import { Button } from "@workspace/ui/components/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@workspace/ui/components/dialog"
export function DialogControlled() {
const [open, setOpen] = React.useState(false)
const [saved, setSaved] = React.useState(false)
return (
<div className="flex flex-col items-center gap-3">
<Button variant="outline" onClick={() => setOpen(true)}>
Share with doctor
</Button>
{saved ? (
<p className="text-xs text-muted-foreground">Report shared.</p>
) : null}
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Share report</DialogTitle>
<DialogDescription>
Your doctor gets a read-only link to the October panel.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button
onClick={() => {
setSaved(true)
setOpen(false)
}}
>
Share
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}
Props
These are the props you set most often. Unlisted props pass through to the underlying element.
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | false | Open on first render. |
open | boolean | — | Open state when controlled. |
onOpenChange | (open) => void | — | Called when the dialog opens or closes. |
modal | boolean or "trap-focus" | true | Block interaction with the page behind. "trap-focus" traps focus but leaves page scroll and clicks alone. |
DialogContent
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | true | Show the X button in the corner. |
className | string | — | Classes merged onto the window. |
DialogFooter
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | false | Add an outline Close button. |