Dialog

A general-purpose modal for non-critical flows, forms, and custom interactions.

This component uses Radix UI
This component uses shadcn/uiDocs

Dialog is a flexible, general-purpose modal used for bespoke interactions such as forms, pickers, multi-step flows, or displaying non-urgent information. Unlike confirmation-focused dialogs, it is designed to be safely dismissible and does not force an explicit decision.

Dialog can be closed by clicking outside the modal or pressing the Escape key, making it suitable for workflows where cancellation is expected and low-risk.

Use Dialog when you need full control over layout, content, and behavior, and the interaction does not involve a critical or destructive action.

For confirmations or warnings, try to use an existing component:

See Modality for guidance on choosing the appropriate dialog pattern.

Loading...
import {
  Button,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogSection,
  DialogSectionSeparator,
  DialogTitle,
  DialogTrigger,
  Input,
  Label,
} from 'ui'
 
export function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button>Show Dialog</Button>
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>Make changes to your profile here.</DialogDescription>
        </DialogHeader>
        <DialogSectionSeparator />
        <DialogSection className="space-y-4">
          <div>
            <Label htmlFor="name">Name</Label>
            <Input id="name" defaultValue="Pedro Duarte" />
          </div>
          <div>
            <Label htmlFor="username">Username</Label>
            <Input id="username" defaultValue="@peduarte" />
          </div>
        </DialogSection>
        <DialogFooter>
          <Button variant="primary">Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Usage

import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '@/components/ui/dialog'
<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Project settings</DialogTitle>
      <DialogDescription>
        Update configuration options for this project. Changes can be discarded at any time.
      </DialogDescription>
    </DialogHeader>
    {/* Custom content goes here */}
  </DialogContent>
</Dialog>

Guidelines

  • Use for non-critical interactions: Dialog is appropriate when dismissal has no serious consequences.
  • Design for cancellation: Assume users may close the dialog without completing the action.
  • Guard dirty forms on dismissal: For form dialogs, keep Dialog dismissible but intercept close attempts when the form is dirty and show a discard-confirmation dialog (for example, Studio's DiscardChangesConfirmationDialog pattern via useConfirmOnClose).
  • Keep focus contained: Dialog content should remain scoped to a single task or flow.
  • Avoid destructive confirmations: If the dialog’s primary purpose is to confirm a risky action, use a confirmation-focused pattern instead.
  • Compose freely: Dialog is intentionally unopinionated. Build custom layouts, forms, or step-based flows as needed.

Examples

Optional details

Use DialogDisclosure to reveal optional help or context inside a dialog. Put the trigger in the dialog body above the footer, and add a section separator when the disclosure follows other content. Keep the primary action visible while the details are open or closed.

Collapsible provides the open and closed state. DialogDisclosure adds the trigger and content presentation used in dialogs. Use Accordion when several related sections each need a heading and expandable content.

Loading...
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogSection,
  DialogSectionSeparator,
  DialogTitle,
  DialogTrigger,
} from 'ui'
import {
  DialogDisclosure,
  DialogDisclosureContent,
  DialogDisclosureTrigger,
} from 'ui-patterns/DialogDisclosure'
 
export function DialogDisclosureDemo() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button>Export results</Button>
      </DialogTrigger>
      <DialogContent size="small">
        <DialogHeader>
          <DialogTitle>Export results</DialogTitle>
        </DialogHeader>
        <DialogSectionSeparator />
        <DialogSection>
          <p className="text-sm text-foreground-light">
            All 250 rows will be downloaded as a CSV file.
          </p>
        </DialogSection>
        <DialogSectionSeparator />
        <DialogDisclosure>
          <DialogDisclosureTrigger className="px-4 py-4 md:px-5">
            What’s included
          </DialogDisclosureTrigger>
          <DialogDisclosureContent>
            <DialogSection className="pt-1 text-sm text-foreground-light space-y-3">
              <p>The file includes column names in the first row, followed by the query results.</p>
              <ul className="list-disc pl-5 space-y-1">
                <li>Columns keep the same order as the results table.</li>
                <li>Empty fields represent null values.</li>
                <li>Values containing commas or line breaks are enclosed in double quotes.</li>
              </ul>
              <p>Open the file in a spreadsheet or import it into another tool.</p>
            </DialogSection>
          </DialogDisclosureContent>
        </DialogDisclosure>
        <DialogFooter>
          <DialogClose asChild>
            <Button>Cancel</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button variant="primary">Download CSV</Button>
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Custom close button

Loading...

Centered behavior

You can control whether the dialog is centered by passing centered={false} to the DialogContent component.

<Dialog>
  <ContextMenuTrigger>Click here</ContextMenuTrigger>
  <DialogContent centered={false}>
    {/*
     * Content in here
     */}
  </DialogContent>
</Dialog>
Loading...

Notes

To activate the Dialog component from within a Context Menu or Dropdown Menu, you must encase the Context Menu or Dropdown Menu component in the Dialog component. For more information, refer to the linked issue here.

<Dialog>
  <ContextMenu>
    <ContextMenuTrigger>Show Menu</ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>Open</ContextMenuItem>
      <ContextMenuItem>Download</ContextMenuItem>
      <DialogTrigger asChild>
        <ContextMenuItem>
          <span>Show Dialog</span>
        </ContextMenuItem>
      </DialogTrigger>
    </ContextMenuContent>
  </ContextMenu>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Edit profile</DialogTitle>
      <DialogDescription>Make changes to your profile here.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <Button>Save changes</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>