Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by imskyleen
Edit on GitHub

Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.

Installation

bash
npx shadcn@latest add @redpanda/dialog
bash
pnpm dlx shadcn@latest add @redpanda/dialog
bash
yarn dlx shadcn@latest add @redpanda/dialog
bash
bunx shadcn@latest add @redpanda/dialog
bash
nubx shadcn@latest add @redpanda/dialog
bash
aube dlx shadcn@latest add @redpanda/dialog

When to use

Use this decision tree to determine when to use the Dialog component:

Usage

tsx
<Dialog>  <DialogTrigger>Open Dialog</DialogTrigger>  <DialogContent>    <DialogHeader>      <DialogTitle>Title</DialogTitle>      <DialogDescription>Description</DialogDescription>    </DialogHeader>    <p>Dialog Content</p>    <DialogFooter>      <button>Close</button>    </DialogFooter>  </DialogContent></Dialog>

Props

Examples

Default

Delete

No Header

Skip the header when the dialog body stands on its own — the close button stays at the top-right and the top border is omitted since there’s nothing to separate from.

Omit the footer when the dialog is informational and doesn’t require explicit actions beyond dismissing.

Scrolling content

DialogContent caps at max-h-[85vh] and DialogBody is the scrollable region — long content gets a pinned header/footer with an internal scrollbar for free. When the body overflows, fading top/bottom shadows appear automatically to signal “more above/below”; opt out with <DialogBody scrollShadow={false}>.

Fixed height

Pass height="sm" | "md" | "lg" | "xl" on DialogContent to lock the dialog to a stable pixel height (clamped to 85vh). This is useful for multi-view dialogs — wizards, step flows, gallery → detail — where the container shouldn’t reflow as the user navigates between views.

Animated auto height

In the default height="auto" mode, the dialog smoothly animates between natural heights when its content changes — toggling sections, async-loaded data, expanding details. This height transition is powered by Motion. No prop opt-in needed; it’s automatic for auto-height dialogs and respects prefers-reduced-motion.

Use body-only dialogs sparingly — without a visible header the dialog still needs an accessible name, so render a DialogTitle with className="sr-only" for screen readers. The dialog owns close-button clearance for a direct DialogBody when no DialogHeader is present. Do not add body-padding overrides: the standard headerless inset keeps text clear of the close action. Headered dialogs and dialogs with showCloseButton={false} retain their existing body padding.

When the footer pairs metadata or status text (e.g. “Changes are saved automatically”) with a single dismiss action, use justify="between" so the text sits on the left and the button on the right. The footer centers items vertically by default, so the text baseline aligns cleanly with the button.

Component Props

DialogContent

PropType
size?'sm' | 'md' | 'lg' | 'xl' | 'full'

Maximum width breakpoint of the dialog.

Type'sm' | 'md' | 'lg' | 'xl' | 'full'
Default'md'
variant?'standard' | 'centered' | 'destructive'

Visual treatment of the dialog frame.

Type'standard' | 'centered' | 'destructive'
Default'standard'
height?'auto' | 'sm' | 'md' | 'lg' | 'xl'

Vertical sizing. `auto` grows with content (capped at 85vh) and animates between natural heights when content changes. The fixed sizes lock the dialog to a stable pixel height (each clamped to 85vh) so multi-view dialogs don't reflow between steps.

Type'auto' | 'sm' | 'md' | 'lg' | 'xl'
Default'auto'
showCloseButton?boolean

Render the top-right close affordance.

Typeboolean
Defaulttrue
showOverlay?boolean

Render the dimmed backdrop behind the dialog.

Typeboolean
Defaulttrue

DialogBody

PropType
padding?'none' | 'sm' | 'md' | 'lg'

Padding around the body content. The inner content is wrapped so scroll shadows can sit flush against the body edges — set `padding` here rather than overriding via `className`, which targets the outer scroll container.

Type'none' | 'sm' | 'md' | 'lg'
Default'md'
spacing?'none' | 'sm' | 'md' | 'lg'

Vertical gap between direct children of the body.

Type'none' | 'sm' | 'md' | 'lg'
Default'md'
scrollShadow?boolean

Show fading top/bottom shadows when the body overflows, cueing the user that there is more content above or below.

Typeboolean
Defaulttrue

Keyboard interactions

Key Action
Enter / Space on DialogTrigger Opens the dialog and moves focus to the first focusable element inside DialogContent.
Tab / Shift+Tab Cycles focus within the dialog — focus is trapped while open.
Escape Closes the dialog and restores focus to the trigger.
Click outside DialogContent Closes the dialog (modal scrim dismiss).

Accessibility

  • Render a DialogTitle (or a visually-hidden one) — Base UI enforces this with a development warning. The dialog is labelled via aria-labelledby pointing at the title.
  • Add a DialogDescription when the dialog’s purpose isn’t obvious from the title; it is wired up via aria-describedby.
  • Focus is trapped inside the popup while open and returned to the trigger on close. Background content is marked aria-hidden and made inert so assistive tech doesn’t reach it.
  • onOpenChange uses Base UI’s (open: boolean, eventDetails) => void signature. The second argument carries the close reason (e.g. 'escape-key', 'outside-press', 'close-press') and is optional to consume — existing Radix-style single-argument handlers keep working unchanged.

Controlled vs uncontrolled

Uncontrolled — let the dialog own open state, react via onOpenChange:

tsx
<Dialog onOpenChange={(open) => console.log('open:', open)}>  <DialogTrigger>Open</DialogTrigger>  <DialogContent>{/* ... */}</DialogContent></Dialog>

Controlled — drive open from parent state, e.g. to open the dialog from a menu item or remote action:

tsx
const [open, setOpen] = useState(false);
<Dialog open={open} onOpenChange={setOpen}>  <DialogTrigger>Open</DialogTrigger>  <DialogContent>    <DialogFooter>      <Button onClick={() => setOpen(false)}>Done</Button>    </DialogFooter>  </DialogContent></Dialog>;

Credits

  • We use Base UI for the dialog component.
  • We take our inspiration from Shadcn UI for the dialog style.
  • The animated auto-height transition is powered by Motion.

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.1.0Bulk install experience and Dialog polish.#119
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
See full history →

Last updated on

Was this page helpful?