Dialog
Dialog opens a modal window above the page. Focus is trapped inside it, Escape closes it,
and focus returns to the trigger when it closes.
import { Dialog } from '@beyul/sulmu-ui'Examples
Section titled “Examples”With a trigger
Section titled “With a trigger”Pass the element that opens the dialog as trigger. Two buttons in footer share the width
equally.
import { Button, Dialog } from '@beyul/sulmu-ui'
export default function Example() { return ( <Dialog title="Add Client" description="Eu feugiat enim velit vitae sem diam sed faucibus ac." trigger={<Button>Add client</Button>} footer={ <> <Button variant="secondary" size="lg"> Cancel </Button> <Button size="lg">Add</Button> </> } > Dialog content goes here. </Dialog> )}Controlled, with a form
Section titled “Controlled, with a form”Control it with open and onOpenChange when something else has to close it, such as a
successful save.
import { Button, Dialog, Divider, Field, Grid, Input, Select } from '@beyul/sulmu-ui'import { useState } from 'react'
const priorities = [ { value: 'low', label: 'Low' }, { value: 'medium', label: 'Medium' }, { value: 'high', label: 'High' },]
export default function Example() { const [open, setOpen] = useState(false) return ( <Dialog open={open} onOpenChange={(d) => setOpen(d.open)} title="Add Client" trigger={<Button variant="secondary">Open form dialog</Button>} footer={ <> <Button variant="secondary" size="lg" onClick={() => setOpen(false)}> Cancel </Button> <Button size="lg" onClick={() => setOpen(false)}> Add </Button> </> } > <Divider label="Basic info" /> <Grid columns={2} gap={6}> <Field label="Client Name" required> <Input defaultValue="Beyond Borders Agency" /> </Field> <Field label="Priority" required> <Select items={priorities} defaultValue={['low']} /> </Field> </Grid> </Dialog> )}Confirmation
Section titled “Confirmation”hideClose removes the X button and closeOnInteractOutside={false} stops a click outside
from closing it, for dialogs that need an explicit choice.
import { Button, Dialog } from '@beyul/sulmu-ui'import { useState } from 'react'
export default function Example() { const [open, setOpen] = useState(false) return ( <Dialog size="sm" hideClose closeOnInteractOutside={false} open={open} onOpenChange={(d) => setOpen(d.open)} title="Delete this stage?" description="The 4 deals in it move to the previous stage. This cannot be undone." trigger={<Button variant="destructive">Delete stage</Button>} footer={ <> <Button variant="secondary" onClick={() => setOpen(false)}> Keep stage </Button> <Button variant="destructive" onClick={() => setOpen(false)}> Delete </Button> </> } /> )}| Prop | Type | Default | Description |
|---|---|---|---|
title* | ReactNode | - | Required: names the dialog for assistive tech. |
description | ReactNode | - | |
trigger | ReactElement<unknown, string | JSXElementConstructor<any>> | - | Optional element that opens the dialog. Or control it with open and onOpenChange. |
footer | ReactNode | - | Actions. Two buttons share the width equally, as in the mockups. |
size | "sm" | "md" | "lg" | md | sm 400, md 576 (default), lg 768. |
hideClose | boolean | - | Hide the close (X) button, for dialogs that need an explicit choice. |
className | string | - | |
open | boolean | - | The controlled open state of the dialog |
defaultOpen | boolean | - | The initial open state of the dialog when rendered. Use when you don't need to control the open state of the dialog. |
onOpenChange | ((details: OpenChangeDetails) => void) | - | Function to call when the dialog's open state changes |
closeOnInteractOutside | boolean | - | Whether to close the dialog when the outside is clicked |
closeOnEscape | boolean | - | Whether to close the dialog when the escape key is pressed |
The remaining props of Ark UI’s Dialog.Root are accepted too.
Accessibility and usage
Section titled “Accessibility and usage”titleis required; it names the dialog for assistive tech.descriptionis linked as its description.- Sizes:
smis 400px wide,md576px andlg768px. - The content is mounted when the dialog opens and removed when it closes, so state inside it (including form values) starts fresh each time.
- The dialog is rendered in a portal at the end of
<body>.