Dialog
Displays modal content over the page.
Edit profile
Make changes to your profile. Close the dialog when you are done.
import ilha from "ilha";
import { Button, Dialog } from "areia";
export default ilha.render(() => (
<Dialog
trigger={<Button>Open dialog</Button>}
contentClass="grid gap-4 p-6"
content={
<>
<div class="grid gap-2">
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
Make changes to your profile. Close the dialog when
you are done.
</Dialog.Description>
</div>
<div class="flex justify-end gap-2">
<Dialog.Close>
<Button variant="secondary">Cancel</Button>
</Dialog.Close>
<Dialog.Close>
<Button variant="primary">Save</Button>
</Dialog.Close>
</div>
</>
}
/>
));Import
import { Dialog } from "areia";
Usage
Use Dialog for an interactive dialog. The composition-first API accepts Dialog.Trigger, Dialog.Portal, Dialog.Overlay, and Dialog.Content as children. The trigger and content props remain available as shortcuts.
Dialog title
Helpful supporting text for the dialog.
import ilha from "ilha";
import { Button, Dialog } from "areia";
export default ilha.render(() => (
<Dialog>
<Dialog.Trigger>
<Button>Open</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content class="grid gap-4 p-6">
<Dialog.Title>Dialog title</Dialog.Title>
<Dialog.Description>
Helpful supporting text for the dialog.
</Dialog.Description>
<Dialog.Close>
<Button>Close</Button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
));Examples
Ilha + portaled overlays (dialog, popover, dropdown, hover-card)
@areia/slots moves portal wrappers (dialog-portal, popover/dropdown positioners, etc.) to document.body when overlays open. That subtree is outside the Ilha island host element. The same constraints apply to Popover, Dropdown, and HoverCard when portal is enabled (default).
Ilha’s renderer morphs only the island host (data-ilha). After the portal is teleported, reactive updates from the owning island (bound inputs, each(results), nested data-ilha-slot children inside dialog-content) may not reach portaled nodes. You can see a frozen snapshot (empty search field, command-list still hidden, or an unhydrated nested island placeholder).
Supported patterns today
| Approach | When to use |
|---|---|
| One island owns everything | open, query, and list markup live in a single parent .render. Prefer the shortcut content={…} or composed Dialog.Content without a separate child island only inside the portal. |
| Imperative bridge (escape hatch) | onPortalMounted(container) on portaled primitives — Dialog, Popover, Dropdown, HoverCard, Tooltip, ContextMenu, Combobox (slots), Select (slots). Use to sync portaled DOM (e.g. command palette bridge). Remove when Ilha can patch teleported subtrees. |
Dialog.Static | SSR / docs only — not for signal-driven UI inside a portaled panel. |
Close controls: Dialog.Close and Popover.Close with as="button" (default) wrap icon-only children (SVG / Icon) in <button type="button" data-slot="…-close"> instead of putting the close slot on the SVG alone.
Alert dialog
Use role: "alertdialog" for confirmation flows that require explicit acknowledgment. Alert dialogs do not close on outside click by default.
Delete project?
This action cannot be undone. The project and all related data will be permanently deleted.
import ilha from "ilha";
import { Button, Dialog } from "areia";
export default ilha.render(() => (
<Dialog
role="alertdialog"
trigger={
<Button variant="destructive">Delete project</Button>
}
contentClass="grid gap-4 p-6"
content={
<>
<div class="grid gap-2">
<Dialog.Title>Delete project?</Dialog.Title>
<Dialog.Description>
This action cannot be undone. The project and all
related data will be permanently deleted.
</Dialog.Description>
</div>
<div class="flex justify-end gap-2">
<Dialog.Close>
<Button variant="secondary">Cancel</Button>
</Dialog.Close>
<Dialog.Close>
<Button variant="destructive">Delete</Button>
</Dialog.Close>
</div>
</>
}
/>
));Sizes
import ilha from "ilha";
import { Button, Dialog } from "areia";
const sizes = ["sm", "base", "lg", "xl"] as const;
export default ilha.render(() => (
<div class="flex flex-wrap gap-2">
{sizes.map((size) => (
<Dialog
size={size}
trigger={<Button variant="secondary">{size}</Button>}
contentClass="grid gap-4 p-6"
content={
<>
<Dialog.Title>{size} dialog</Dialog.Title>
<Dialog.Description>
Dialogs constrain their minimum width by size.
</Dialog.Description>
<Dialog.Close>
<Button>Close</Button>
</Dialog.Close>
</>
}
/>
))}
</div>
));Close behavior
Configure outside-click, Escape, initial open state, and scroll locking through root options.
Explicit close
Clicking outside this dialog will not close it.
import ilha from "ilha";
import { Button, Dialog } from "areia";
export default ilha.render(() => (
<Dialog
closeOnClickOutside={false}
closeOnEscape
lockScroll
trigger={<Button>Open locked dialog</Button>}
contentClass="grid gap-4 p-6"
content={
<>
<Dialog.Title>Explicit close</Dialog.Title>
<Dialog.Description>
Clicking outside this dialog will not close it.
</Dialog.Description>
<Dialog.Close>
<Button>Close</Button>
</Dialog.Close>
</>
}
/>
));Prop shortcut
For simple dialogs, pass trigger and content. Areia generates the trigger, portal, overlay, and content slots for you. With Ilha, keep reactive content (or composed Dialog.Content children) in the island .render callback so lists and inputs update when signals change; Dialog.Static pre-renders markup for SSR-only shortcuts.
Shortcut API
Use props when you do not need to customize the slot structure.
import ilha from "ilha";
import { Button, Dialog } from "areia";
export default ilha.render(() => (
<Dialog
trigger={<Button>Open shortcut dialog</Button>}
contentClass="grid gap-4 p-6"
content={
<>
<Dialog.Title>Shortcut API</Dialog.Title>
<Dialog.Description>
Use props when you do not need to customize the slot
structure.
</Dialog.Description>
<Dialog.Close>
<Button>Done</Button>
</Dialog.Close>
</>
}
/>
));API Reference
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
trigger | unknown | — | Custom trigger markup. |
children | unknown | — | Composed dialog slots, or trigger content when no Dialog.Content child is present. |
content | unknown | — | Dialog panel content. |
size | "sm" | "base" | "lg" | "xl" | "base" | Dialog width. |
role | "dialog" | "alertdialog" | "dialog" | Dialog semantic role. |
defaultOpen | boolean | false | Initial open state. |
closeOnClickOutside | boolean | true (false for alert dialogs) | Close when clicking outside content. |
closeOnEscape | boolean | true | Close when pressing Escape. |
lockScroll | boolean | true | Lock body scroll while open. |
onOpenChange | (open: boolean) => void | — | Called when open state changes. |
onPortalMounted | (container: HTMLElement) => void | — | After portal moves to document.body on open. Also on Popover, Dropdown, HoverCard; AlertDialog via slots createAlertDialog. |
Compound exports
Dialog.TriggerDialog.PortalDialog.OverlayDialog.ContentDialog.TitleDialog.DescriptionDialog.Close