Usage
import { Dialog } from "chakra-ui-solid";<Dialog.Root>
<Dialog.Trigger />
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.CloseTrigger />
<Dialog.Header>
<Dialog.Title />
</Dialog.Header>
<Dialog.Body />
<Dialog.Footer />
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>Examples
Sizes
Use the size prop to change the size of the dialog component.
Cover
Use the size="cover" prop to make the dialog component cover the entire screen while revealing a
small portion of the page behind.
Fullscreen
Use the size="full" prop to make the dialog component take up the entire screen.
Responsive Size
Use responsive values for the size prop to make the dialog adapt to different screen sizes.
We recommend using exact breakpoints values instead of using a base to ensure styles are properly
contained.
// ❌ Might cause a style leak between the breakpoints
<Dialog.Root size={{ base: "full", md: "lg" }}>{/* ... */}</Dialog.Root>
// Works ✅
<Dialog.Root size={{ mdDown: "full", md: "lg" }}>{/* ... */}</Dialog.Root>size is a recipe variant, not a style prop, so a responsive value needs the opt-in that
pre-generates it at every breakpoint. Without it the dialog renders with no size rules at all and
nothing errors:
export default defineChakraConfig({
responsive: { dialog: ["size"] },
include: [...],
});defineChakraConfig covers the three grains it accepts.
Placement
Use the placement prop to change the placement of the dialog component.
Controlled
Use the open and onOpenChange prop to control the visibility of the dialog component.
Store
An alternative way to control the dialog is to use the RootProvider component and the
createDialog store hook.
This way you can access the dialog state and methods from outside the dialog.
Context
Use the Dialog.Context component to access the dialog state and methods from outside the dialog.
Its render prop is called in the part’s own body, which is not a tracking scope — so it has to return JSX. A callback returning a bare ternary reads the state untracked and freezes on the value it had at mount.
Nested Dialogs
You can nest dialogs by using the Dialog.Root component inside another Dialog.Root component.
Open From Popover
Dialogs can be triggered from within a popover. The dialog will appear above the popover thanks to the unified z-index system.
Initial Focus
Use the initialFocusEl prop to set the initial focus of the dialog component.
Inside Scroll
Use the scrollBehavior=inside prop to change the scroll behavior of the dialog when its content
overflows.
Outside Scroll
Use the scrollBehavior=outside prop to change the scroll behavior of the dialog when its content
overflows.
Motion Preset
Use the motionPreset prop to change the animation of the dialog component.
Alert Dialog
Set the role: "alertdialog" prop to change the dialog component to an alert dialog.
Close Button Outside
Here’s an example of how to customize the Dialog.CloseTrigger component to position the close
button outside the dialog component.
Non-Modal Dialog
We don’t recommend using a non-modal dialog due to the accessibility concerns they present. In event you need it, here’s what you can do:
- set the
modalprop tofalse - set
pointerEventstononeon theDialog.Positionercomponent - (optional)set the
closeOnInteractOutsideprop tofalse
DataList
Here’s an example of how to compose the dialog component with the DataList component.
Props
Root
| Prop | Default | Type |
|---|---|---|
children | — | JSX.Element |
closeOnEscape | true | booleanWhether Escape closes the dialog. |
closeOnInteractOutside | true | booleanWhether a click outside the content closes the dialog. |
defaultOpen | — | booleanThe open state a fresh, uncontrolled dialog starts in. |
defaultTriggerValue | — | string | nullThe active trigger a fresh, uncontrolled dialog starts with. |
finalFocusEl | — | () => HTMLElement | nullThe element that takes focus when the dialog closes. |
id | — | stringSeeds every id the machine hands out — the content is `dialog:{id}:content`, the trigger `dialog:{id}:trigger`, and so on for all seven parts. Defaults to a generated id. Pass `ids` to name the elements themselves. |
ids | — | DialogElementIdsOverride individual element ids, for pointing an ARIA relationship at a specific one. |
immediate | — | booleanApply an open/close change in the same frame rather than the next one. |
initialFocusEl | — | () => HTMLElement | nullThe element that takes focus when the dialog opens, instead of the content itself. |
lazyMount | true | booleanKeep the content out of the DOM entirely until the dialog first opens. |
modal | true | booleanWhether the content blocks the page behind it — pointer events off, everything else `aria-hidden`. |
motionPreset | 'scale' | ConditionalValue<| 'scale' | 'slide-in-bottom' | 'slide-in-top' | 'slide-in-left' | 'slide-in-right' | 'none' | PresetVariant<'dialog', 'motionPreset'>>Which pair of enter/exit animations the surface plays. |
onEscapeKeyDown | — | (event: KeyboardEvent) => voidCalled when Escape is pressed, before the dialog decides whether to close. |
onExitComplete | — | VoidFunctionCalled once the exit animation has finished and the content is fully gone. |
onFocusOutside | — | (event: DialogFocusOutsideEvent) => voidCalled when focus moves outside the content. |
onInteractOutside | — | (event: DialogInteractOutsideEvent) => voidCalled on either of the two above. |
onOpenChange | — | (details: DialogOpenChangeDetails) => voidCalled whenever the open state changes, from either side. |
onPointerDownOutside | — | (event: DialogPointerDownOutsideEvent) => voidCalled on a pointer press outside the content. |
onRequestDismiss | — | dialog.Props['onRequestDismiss']Called when a parent layer closing takes this one with it. |
onTriggerValueChange | — | (details: DialogTriggerValueChangeDetails) => voidCalled whenever the active trigger changes. |
open | — | booleanThe controlled open state. Pass `undefined` for uncontrolled. |
persistentElements | — | Array<() => Element | null>Elements that keep their pointer events and do not count as "outside" — a toast, a tour step. |
placement | 'top' | ConditionalValue<'center' | 'top' | 'bottom' | PresetVariant<'dialog', 'placement'>>Where the surface sits in the viewport. |
present | — | booleanDrive the content's presence from something other than the dialog's own `open` state — an escape hatch for animating the surface independently of the machine. **It reaches the content and the positioner only.** The backdrop builds its own presence straight from `open` and ignores this, so `present={true}` over a closed dialog shows the surface with no scrim behind it. That asymmetry is Ark's, reproduced rather than corrected — `DialogBackdrop` hard-codes `present: dialog.open` where `DialogRoot` merges this prop in. Resolved with `??`, so a wrapper forwarding an unset `present={props.present}` falls back to `open`. That is parity rather than a fix: Ark's `createSplitProps` drops an `undefined` before the merge ever sees it, so the React version resolves this key by value too. |
preventScroll | true | booleanWhether the page behind the dialog stops scrolling while it is open. |
restoreFocus | — | booleanWhether focus returns to whatever had it before the dialog opened. |
role | 'dialog' | 'dialog' | 'alertdialog'The ARIA role the content carries. `alertdialog` is the interruptive one, for a destructive confirmation. |
scrollBehavior | 'outside' | ConditionalValue<'inside' | 'outside' | PresetVariant<'dialog', 'scrollBehavior'>>Whether a dialog taller than the viewport scrolls its own body or the page behind it. |
size | 'md' | ConditionalValue<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'cover' | 'full' | PresetVariant<'dialog', 'size'>>How wide the surface is allowed to grow. `cover` and `full` also change its height. |
skipAnimationOnMount | false | booleanSuppress the enter animation on the very first open, so a `defaultOpen` dialog does not animate in as the page loads. |
trapFocus | true | booleanWhether Tab is confined to the content while it is open. |
triggerValue | — | string | nullThe controlled active trigger, for one dialog shared by several triggers. |
unmountOnExit | true | booleanTake the content back out of the DOM once it has closed and its exit animation has finished, rather than leaving it there hidden. |
Plus the three every component takes: as, render and unstyled.