Dialog
Modal dialogs with springy transitions that trap focus, lock scrolling, and restore focus on overlay click or Escape.
Installation
Add component
pnpm dlx @elyri/cli@latest add dialogExamples
Basic
Structure with Dialog.Header / Body / Footer; a corner close button is built in. Use asChild to render a Button.
import { Button } from './components/elyri/Button';
import { Dialog } from './components/elyri/Dialog';
export function Example() {
return (
<Dialog>
<Dialog.Trigger asChild>
<Button variant="danger">Delete project</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Delete project</Dialog.Title>
<Dialog.Description>
This permanently removes the project and everything in it. This cannot be undone.
</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Dialog.Close asChild>
<Button variant="secondary">Cancel</Button>
</Dialog.Close>
<Dialog.Close asChild>
<Button variant="danger">Delete</Button>
</Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog>
);
}Scrolling content
Only the body scrolls while the header and footer stay put, with dividers appearing on overflow.
import { Button } from './components/elyri/Button';
import { Dialog } from './components/elyri/Dialog';
export function Example({ paragraphs }: { paragraphs: string[] }) {
return (
<Dialog>
<Dialog.Trigger asChild>
<Button variant="secondary">Read terms</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Terms of service</Dialog.Title>
<Dialog.Description>Please read carefully before continuing.</Dialog.Description>
</Dialog.Header>
<Dialog.Body>
{paragraphs.map((text) => (
<p key={text}>{text}</p>
))}
</Dialog.Body>
<Dialog.Footer>
<Dialog.Close asChild>
<Button variant="secondary">Decline</Button>
</Dialog.Close>
<Dialog.Close asChild>
<Button>Accept</Button>
</Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog>
);
}Blocked dismissal
With overlay click and Escape disabled, stray dismiss attempts make the surface bump instead.
import { Button } from './components/elyri/Button';
import { Dialog } from './components/elyri/Dialog';
export function Example() {
return (
<Dialog>
<Dialog.Trigger asChild>
<Button variant="secondary">Start upload</Button>
</Dialog.Trigger>
<Dialog.Content closeOnOverlayClick={false} closeOnEscape={false} showCloseButton={false}>
<Dialog.Header>
<Dialog.Title>Upload in progress</Dialog.Title>
<Dialog.Description>Try clicking outside or pressing Escape — use the button to finish.</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Dialog.Close asChild>
<Button>Done</Button>
</Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial open state when uncontrolled |
onOpenChange | (open: boolean) => void | — | Called whenever the open state changes |
modal | boolean | true | Modal: locks scroll and traps focus, defaults to true |
Dialog.Trigger | ButtonHTMLAttributes | — | Trigger button, wired with aria-haspopup / aria-expanded |
asChild | boolean | false | On Trigger / Close: merge behaviour onto the single child, e.g. <Button> |
Dialog.Content | HTMLAttributes | — | The dialog surface, rendered in a portal on body |
closeOnOverlayClick | boolean | true | Close on overlay click, defaults to true; bumps when false |
closeOnEscape | boolean | true | Close on Escape, defaults to true; bumps when false |
showCloseButton | boolean | true | Show the corner close button, defaults to true |
closeLabel | string | 'Close' | Accessible name of the corner close button |
Dialog.Header | HTMLAttributes | — | Header holding the Title and Description |
Dialog.Body | HTMLAttributes | — | Scrollable body with overflow dividers |
Dialog.Footer | HTMLAttributes | — | Footer actions, right-aligned and stacked on narrow screens |
Dialog.Title | HTMLAttributes | — | Title, used as the accessible name |
Dialog.Description | HTMLAttributes | — | Description, linked through aria-describedby |
Dialog.Close | ButtonHTMLAttributes | — | A button that closes the dialog |