Dialog
Generic modal dialog primitive built on react-aria-components. Domain-agnostic
and token-driven — a feature composes it into create/edit dialogs, a mobile
sheet, etc. position/size variants cover a centered modal and a bottom sheet.
import { Dialog } from "@evolve-storefront/ui/dialog";Design notes
Every overlay in ui is RAC for one reason: a modal that locks interaction by
setting pointer-events: none on <body> makes any other library's portalled
popup unclickable inside it, because the popup is a sibling of the dialog rather
than a descendant. RAC's overlays know about each other, so a Select opened
inside a Dialog behaves. Do not reintroduce a second overlay library.
Preview
Usage
The edit dialog, opened from its trigger. position="bottom" anchors it to the bottom
edge as a mobile sheet, size="lg" widens it, and hideCloseButton drops the corner
button where the footer's DialogClose is the way out: all three are controls here.
<Dialog> <DialogTrigger asChild> <Button variant="secondary">{t("editList")}</Button> </DialogTrigger> <DialogContent closeLabel={t("close")}> <DialogHeader> <DialogTitle>{t("editListTitle")}</DialogTitle> <DialogDescription>{t("editListDescription")}</DialogDescription> </DialogHeader> <DialogFooter> <DialogClose asChild> <Button variant="secondary">{t("cancel")}</Button> </DialogClose> <Button onClick={save}>{t("save")}</Button> </DialogFooter> </DialogContent></Dialog>;Props
| Prop | Type | Description |
|---|---|---|
open? | boolean | Controlled open state. Omit to let the dialog own it. |
defaultOpen? | boolean | |
onOpenChange? | ((open: boolean) => void) | |
children? | ReactNode |
Change the layout
Pass children to Dialog to arrange its parts yourself. Every part is exported from @evolve-storefront/ui/dialog.
<Dialog> <DialogTrigger asChild> <Button variant="secondary">{t("editList")}</Button> </DialogTrigger> <DialogContent closeLabel={t("close")}> <DialogHeader> <DialogTitle>{t("editListTitle")}</DialogTitle> <DialogDescription>{t("editListDescription")}</DialogDescription> </DialogHeader> <DialogFooter> <DialogClose asChild> <Button variant="secondary">{t("cancel")}</Button> </DialogClose> <Button onClick={save}>{t("save")}</Button> </DialogFooter> </DialogContent></Dialog>;DialogCloseCloses the dialog on click.
| Prop | Type | Description |
|---|---|---|
asChild? | boolean |
Any other props go to the root element, like on a plain HTML element.
DialogContentThe modal panel.
| Prop | Type | Description |
|---|---|---|
size? | "sm" | "md" | "lg" | null | |
position? | "center" | "bottom" | null | |
hideCloseButton? | boolean | Hide the built-in top-right close button. |
closeLabel? | string | |
closeIcon? | ReactNode | Replace the close glyph (rung 3). Defaults to the |
overlayClassName? | string | |
onClosed? | (() => void) | Fires once the dialog has closed — the seam for resetting form state. |
children? | ReactNode |
Any other props go to the root element, like on a plain HTML element.
DialogDescriptionRegisters itself so aria-describedby is only set when a description is actually rendered — a dangling reference is worse than none.
No props of its own.
DialogHeaderGroups the title and the description at the top of the panel.
No props of its own.
DialogTitleslot="title" is what RAC reads to label the dialog.
No props of its own.
DialogTriggerOpens the dialog on click.
| Prop | Type | Description |
|---|---|---|
asChild? | boolean |
Any other props go to the root element, like on a plain HTML element.
Examples
With Select
<DialogContent closeLabel={t("close")}> <DialogHeader><DialogTitle>{t("addAddress")}</DialogTitle></DialogHeader> <Select name="country" label={t("country")} placeholder={t("selectCountry")}> <SelectItem id="nl">{t("countries.nl")}</SelectItem> </Select> <DialogFooter>…</DialogFooter></DialogContent>;
