Skip to content
Component installs need the registry setup
VegaStack Design

Responsive Dialog

One overlay API — a centred Dialog from 768px up, a bottom sheet with a drag handle below it.

Status
stable
Since
0.23.116
Accessibility pattern
dialog in both forms; focus trapped; Esc closes

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Responsive Dialog before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify responsive-dialog

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/responsive-dialog

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

The same command installs the registry items it composes: @vegastack/dialog, @vegastack/sheet, @vegastack/use-mobile.

Usage

import {
  ResponsiveDialog,
  ResponsiveDialogBody,
  ResponsiveDialogContent,
  ResponsiveDialogFooter,
  ResponsiveDialogHeader,
  ResponsiveDialogTitle,
} from "@/components/ui/responsive-dialog";

<ResponsiveDialog open={open} onOpenChange={setOpen}>
  <ResponsiveDialogContent size="md">
    <ResponsiveDialogHeader>
      <ResponsiveDialogTitle>Members · Product</ResponsiveDialogTitle>
    </ResponsiveDialogHeader>
    <ResponsiveDialogBody>{list}</ResponsiveDialogBody>
    <ResponsiveDialogFooter>
      <Button>Add</Button>
    </ResponsiveDialogFooter>
  </ResponsiveDialogContent>
</ResponsiveDialog>;

Write the dialog once. From 768px up it is a Dialog of the size you pass (md for share, members and settings dialogs); below that it is a bottom Sheet: full width, as tall as its content up to 85% of the screen, a drag handle at the top, the same header and body, and a footer that stays at the bottom and clears the home indicator. The consumer writes no useIsMobile branch and no layout code. The form follows the viewport while the dialog is closed and is locked while it is open: resizing across the breakpoint never closes it or resets what is inside it, and the other form applies the next time it opens.

Scope

  • Owns: the switch between Dialog and a bottom Sheet, the handle and the safe-area footer.
  • Does not own: the dialog's content, its sizes (they are Dialog's) or the sheet's motion (it is Sheet's).
  • Compose with: anything a Dialog holds — share-01 uses it for Share.

Anatomy

ResponsiveDialog
ResponsiveDialogTrigger
ResponsiveDialogClose
ResponsiveDialogContent — data-slot="responsive-dialog-handle"
ResponsiveDialogHeader
ResponsiveDialogTitle
ResponsiveDialogDescription
ResponsiveDialogBody
ResponsiveDialogFooter

Examples

Sizes

size sizes the dialog form only; the phone sheet is always full width.

API Reference

PropTypeDefaultDescription
breakpointnumber768Viewport width (px) at and above which it is a Dialog; below it, a bottom Sheet.

ResponsiveDialogContent takes DialogContent's props (size, showCloseButton, closeLabel). ResponsiveDialogTrigger, ResponsiveDialogClose, ResponsiveDialogHeader, ResponsiveDialogTitle, ResponsiveDialogDescription, ResponsiveDialogBody and ResponsiveDialogFooter add no props of their own — each takes its Dialog part's props.

Accessibility

  • Both forms are Base UI Dialog: role="dialog" with aria-modal, named by the title, focus trapped inside and returned to the trigger.
  • The drag handle is decorative (aria-hidden); the sheet closes with Escape, its × or a Close.
KeyAction
TabMove between controls inside
EscClose and return focus to the opener
ContractStates tested
Behaviourclosed, open, dialog, sheet, escape-closes
Accessibilitylabeled, keyboard, browser-accessibility-test
Visualdefault, hover, focus

Do / Don't

Do
Use it for any dialog a phone user can open: share, members, move, create.
Don't
Branch on useIsMobile to mount a Dialog or a Sheet yourself.

On this page