Dialog
Modal overlay anchored at viewport center, built on Radix Dialog with header, footer, and built-in close button.
Overview
Dialog is the right primitive for focused decisions or short flows that need to interrupt the user's context: confirmations, single-step forms, detail cards. It ships with overlay, content, header, footer, title, description, and a built-in close button. For longer flows that don't need full focus use Sheet; for inline contextual content use Popover.
The close button is on by default. Set showCloseButton={false} when the dialog renders its own dismiss affordance, or pass showCloseButton on DialogFooter to inject a styled "Close" button there instead.
Preview
Installation
bash npx gremorie@latest add rx-dialog bash pnpm dlx gremorie@latest add rx-dialog bash yarn dlx gremorie@latest add rx-dialog bash bunx --bun gremorie@latest add rx-dialog Usage
import { Button } from "@gremorie/rx-forms";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@gremorie/rx-overlays";
export function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Open dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
API
<Dialog>
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state. |
defaultOpen | boolean | false | Initial open state when uncontrolled. |
onOpenChange | (open: boolean) => void | - | Fired when open state changes. |
modal | boolean | true | Disable outside interaction while open. |
Extends Radix Dialog.Root. Renders no DOM by itself.
<DialogTrigger>
Extends Radix Dialog.Trigger. Pair with asChild to forward styles to a Button or any custom trigger.
<DialogContent>
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | true | Renders the top-right X close button. Disable when supplying a custom dismiss. |
Wraps Radix Dialog.Content inside a Portal with the overlay. Centered at 50/50, capped at sm:max-w-lg, animated open/close.
<DialogHeader>, <DialogFooter>
Plain layout containers (div). Header stacks title and description; footer is flex-col-reverse on mobile, flex-row justify-end on sm+.
DialogFooter accepts showCloseButton (boolean, default false) which appends a styled outline "Close" button wired to DialogClose.
<DialogTitle>, <DialogDescription>
Map to Radix Dialog.Title and Dialog.Description. Both are required by Radix; render with className="sr-only" if you need to hide them visually.
<DialogClose>, <DialogOverlay>, <DialogPortal>
Pass-through wrappers over their Radix counterparts with data-slot attributes. Use DialogClose asChild to dismiss from any custom button inside the content.
Composition
<Dialog>owns the open/close state.<DialogTrigger asChild>wraps any focusable element (typically aButton).<DialogContent>mounts via Portal, draws the overlay, and renders the centered card.- Inside content: header (title + description), body, footer (cancel + primary action).
- Dismissal: the built-in X covers the common case; supplement with explicit
DialogClosebuttons for destructive or stateful flows.
Variations
Confirmation with explicit Cancel
For destructive but reversible actions, pair the X close with an explicit Cancel next to the primary Delete.
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete project</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete project?</DialogTitle>
<DialogDescription>
All tasks, comments and attachments will be removed.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>Form dialog
Embed form fields directly in the body. The dialog handles focus trap; submit closes via state.
<DialogContent>
<form onSubmit={handleSubmit}>
<DialogHeader>
<DialogTitle>Invite teammate</DialogTitle>
<DialogDescription>They'll get an email invite.</DialogDescription>
</DialogHeader>
<Field className="my-4">
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" type="email" required />
</Field>
<DialogFooter>
<Button type="submit">Send invite</Button>
</DialogFooter>
</form>
</DialogContent>Without close button
Hide the X when the dialog is fully driven by the footer (e.g. multi-step wizard where back/next manage progression).
<DialogContent showCloseButton={false}>
{/* Custom step header here */}
</DialogContent>Accessibility
- Role:
role="dialog"witharia-modal="true"(defaultmodalprop). - Keyboard:
Esccloses;Tabcycles focus within the content;Shift+Tabcycles backwards; focus is trapped while open. - Focus management: focus moves to the first focusable element on open and returns to the trigger on close.
- Title:
DialogTitleis mandatory; without it Radix logs a warning. Usesr-onlyto hide visually. - Description: links to content via
aria-describedbyautomatically. - Reduced motion: open/close animations honor
prefers-reduced-motionvia the underlying Tailwind animate utilities.
Related
- Alert Dialog - interruptive confirmation that requires an explicit action.
- Sheet - lateral panel for longer flows.
- Drawer - mobile bottom sheet with gesture dismiss.
- Popover - inline anchored overlay for contextual UI.