Skip to content
Design System
Contact

Modal

Focused tasks and confirmations that block the page until resolved.

ComponentsRadix Dialog

Playground

The panel follows the controls in place; the button opens the same dialog for real, with focus trap, overlay and Esc. Use a modal only when the task must be finished or dismissed before going on.

Preview

Props

Size
Footer
example.tsxtsx
1import { Button } from "@/components/ui/button"
2import {
3 Dialog,
4 DialogClose,
5 DialogContent,
6 DialogDescription,
7 DialogFooter,
8 DialogHeader,
9 DialogTitle,
10 DialogTrigger,
11} from "@/components/ui/dialog"
12import { Input } from "@/components/ui/input"
13import { Label } from "@/components/ui/label"
14
15<Dialog>
16 <DialogTrigger asChild>
17 <Button variant="outline">Invite people</Button>
18 </DialogTrigger>
19 <DialogContent className="sm:max-w-md">
20 <DialogHeader>
21 <DialogTitle>Invite to Northwind portal</DialogTitle>
22 <DialogDescription>Teammates get edit access. You can change their role later.</DialogDescription>
23 </DialogHeader>
24 <div className="grid gap-1.5">
25 <Label htmlFor="invite-email">Email address</Label>
26 <Input id="invite-email" type="email" placeholder="name@company.com" />
27 </div>
28 <DialogFooter>
29 <DialogClose asChild>
30 <Button variant="outline">Cancel</Button>
31 </DialogClose>
32 <Button>Send invite</Button>
33 </DialogFooter>
34 </DialogContent>
35</Dialog>

States

The trigger has the usual button states. The panel has its own: at rest, submitting (inputs and Cancel lock, Esc is ignored), a validation error that keeps the dialog open, and done.

States
  • Defaultrest
  • Hover:hover
  • Focus:focus-visible
  • Loadingaria-busy
  • Disableddisabled
  • Erroraria-invalid
  • Successdata-state=success
Dialog panel states
At rest
Submitting
Validation error
Done, before it closes

Examples

A destructive confirmation, a form that validates and shows progress before closing, and long content with a fixed header and footer.

Confirm a destructive action

Northwind portal

24 files, updated today

Long, scrollable content

Not accepted yet.

Form with validation

Submit empty to see validation, then fill it in.

API

Built on Radix Dialog. Content renders in a portal with an overlay.

PropTypeDefaultDescription
open / onOpenChangeboolean / (open: boolean) => void—Control the dialog. Ignore close requests while a submit is running.
DialogContent classNamestring"sm:max-w-sm"Width: sm:max-w-sm, sm:max-w-md or sm:max-w-lg. Always full width minus 2rem on phones.
showCloseButtonbooleantrueThe X in the corner. Hide it for confirmations that need an explicit choice.
onOpenAutoFocus(event: Event) => void—Prevent and focus another element to change where focus lands.
onEscapeKeyDown(event: KeyboardEvent) => void—Call preventDefault() to block Esc, e.g. while saving.
onInteractOutside(event: Event) => void—Call preventDefault() to keep the dialog open on overlay clicks, e.g. with unsaved input.
DialogFooter showCloseButtonbooleanfalseAdds an outline Close button to the footer.
DialogTitle / DialogDescriptionReactNode—Wired to aria-labelledby and aria-describedby. The title is required.

Accessibility

Keyboard

Next focusable, wraps inside the dialog
Tab
Previous focusable
Shift+Tab
Close and return focus to the trigger
Esc
Submit the form
Enter

Semantics and ARIA

  • Content is a dialog with aria-modal, named by DialogTitle through aria-labelledby and described by DialogDescription through aria-describedby.
  • Focus is trapped while open and the rest of the page is hidden from assistive tech. On close, focus returns to the element that opened it.
  • Destructive confirmations use role="alertdialog", have no corner X and put Cancel first so it receives initial focus.
  • On submit errors, focus moves to the first invalid field, which has aria-invalid and a message that says how to fix it.
  • Scrollable bodies are focusable regions with a name, so keyboard users can scroll them with the arrow keys.
  • Don’t stack dialogs. If a step needs more room, move it to a page or a sheet.