Modal window that asks for focus, such as a confirmation or a short form. Open it with DialogTrigger.
import { Button, Dialog, DialogTrigger } from "@rdloom/react";
export default function DialogConfirmExample() {
return (
<DialogTrigger>
<Button variant="secondary">Discard draft</Button>
<Dialog title="Discard draft?" description="Your changes will be lost." size="sm">
{({ close }) => (
<div className="flex justify-end gap-2">
<Button variant="secondary" onPress={close}>
Keep editing
</Button>
<Button onPress={close}>Discard</Button>
</div>
)}
</Dialog>
</DialogTrigger>
);
}Installation
npx rdloom add dialogCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/dialog.jsonWorks, but without upgrade tracking.
Usage
import { Button, Dialog, DialogTrigger } from "@rdloom/react";
<DialogTrigger>
<Button variant="secondary">Discard draft</Button>
<Dialog title="Discard draft?" description="Your changes will be lost." size="sm">
{({ close }) => (
<div className="flex justify-end gap-2">
<Button variant="secondary" onPress={close}>
Keep editing
</Button>
<Button onPress={close}>Discard</Button>
</div>
)}
</Dialog>
</DialogTrigger>Form
import { Button, Dialog, DialogTrigger, TextField } from "@rdloom/react";
export default function DialogFormExample() {
return (
<DialogTrigger>
<Button>Invite member</Button>
<Dialog title="Invite member" description="They'll get an email with a link.">
{({ close }) => (
<form
className="flex flex-col gap-4"
onSubmit={(e) => {
e.preventDefault();
close();
}}
>
<TextField label="Email" type="email" isRequired autoFocus />
<div className="flex justify-end gap-2">
<Button variant="secondary" onPress={close}>
Cancel
</Button>
<Button type="submit">Send invite</Button>
</div>
</form>
)}
</Dialog>
</DialogTrigger>
);
}Alert
import { Button, Dialog, DialogTrigger } from "@rdloom/react";
export default function DialogAlertExample() {
return (
<DialogTrigger>
<Button variant="danger">Delete project</Button>
{/* alertdialog: screen readers announce it as urgent. Not dismissable by clicking outside. */}
<Dialog title="Delete project?" description="This permanently removes the project and its data." role="alertdialog" size="sm">
{({ close }) => (
<div className="flex justify-end gap-2">
<Button variant="secondary" onPress={close}>
Cancel
</Button>
<Button variant="danger" onPress={close}>
Delete
</Button>
</div>
)}
</Dialog>
</DialogTrigger>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
titlerequiredHeading, also used as the dialog's accessible name. | string | none |
descriptionShort supporting text under the title. | string | none |
sizeMaximum width. | "sm" | "md" | "lg" | "md" |
roleUse alertdialog for destructive or urgent confirmations. | "dialog" | "alertdialog" | "dialog" |
portalContainerDraw the panel and its backdrop inside this element instead of over the whole page. Use it to show a sheet inside a preview, a framed app or a phone mock. The element must be positioned (relative) and clip its overflow. | Element | null | none |
headerEndSmall content at the end of the header line, next to the title, such as a count. | ReactNode | none |
isDismissableCloses on backdrop click. Esc always closes. | boolean | true |
childrenBody and actions. A function receives close(). | ReactNode | ((opts: { close: () => void }) => ReactNode) | none |
Accessibility
Role dialog, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Esc closes
- Tab and Shift+Tab stay inside the dialog
Screen readers announce
- Opening moves focus into the dialog and announces it as a dialog (alert dialog for Delete project) with its title
- The description is read after the title
- Browse/virtual cursor mode can't wander into the page behind the dialog
- Closing announces the button that opened it (focus is back there)
What your code must do
- Focus moves into the dialog on open and returns to the trigger on close
- Content behind the dialog is hidden from assistive technology
- Page scroll is locked while open
Guidelines
Use it when
- Confirming a destructive action
- A short task that must finish before continuing
Avoid it when
- Non-blocking info: use Toast
- Long or complex flows: use a page
Don't
- Dialogs opening other dialogs
- Vague buttons like 'OK' for destructive actions
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-overlay-backdrop--rd-color-surface-raised--rd-color-border-default--rd-color-text-default--rd-color-text-muted--rd-radius-overlay