Dialog
A modal window that appears on top of the main content.
Anatomy
To use the dialog component correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Examples
Learn how to use the Dialog component in your project. Let's take a look at the most basic example
Example not foundExample not foundExample not foundExample not foundControlled
To create a controlled Dialog component, manage the state of the dialog using the open and onOpenChange props:
Example not foundExample not foundExample not foundExample not foundLazy Mount
Lazy mounting is a feature that allows the content of a dialog to be rendered only when the dialog is first opened. This
is useful for performance optimization, especially when dialog content is large or complex. To enable lazy mounting, use
the lazyMount prop on the Dialog.Root component.
In addition, the unmountOnExit prop can be used in conjunction with lazyMount to unmount the dialog content when the
Dialog is closed, freeing up resources. The next time the dialog is activated, its content will be re-rendered.
Example not foundExample not foundExample not foundExample not foundAlert Dialog
For critical confirmations or destructive actions, use role="alertdialog". Alert dialogs differ from regular dialogs
in important ways:
- Automatic focus: The close/cancel button receives focus when opened, prioritizing the safest action
- Requires explicit dismissal: Cannot be closed by clicking outside, only via button clicks or Escape key
Example not foundExample not foundExample not foundExample not foundInitial Focus
Control which element receives focus when the dialog opens using the initialFocusEl prop. This is useful for forms
where you want to focus a specific input field:
Example not foundExample not foundExample not foundExample not foundFinal Focus
Control which element receives focus when the dialog closes using the finalFocusEl prop. By default, focus returns to
the trigger element, but you can specify a different element:
Example not foundExample not foundExample not foundExample not foundNon-Modal Dialog
Use modal={false} to create a non-modal dialog that allows interaction with elements outside of it. This disables
focus trapping, scroll prevention, and pointer blocking, making it useful for auxiliary panels or inspector windows:
Example not foundExample not foundExample not foundExample not foundClose on Interact Outside
Prevent the dialog from closing when clicking outside by setting closeOnInteractOutside={false}. Use the
onInteractOutside event with e.preventDefault() for advanced control:
Example not foundExample not foundExample not foundExample not foundClose on Escape
Prevent the dialog from closing when pressing Escape by setting closeOnEscape={false}. Use the onEscapeKeyDown event
with e.preventDefault() to implement custom behavior like unsaved changes warnings:
Example not foundExample not foundExample not foundExample not foundRender Function
Use the Dialog.Context component to access the dialog's state and methods.
Example not foundExample not foundExample not foundExample not foundRoot Provider
The useDialog hook gives you programmatic access to the dialog's state and methods. Use it with Dialog.RootProvider
when you need to control the dialog from outside its component tree.
Example not foundExample not foundExample not foundExample not foundNote: There are two ways to use the Dialog component: (1)
Dialog.Rootfor declarative usage, or (2)useDialog()+Dialog.RootProviderfor programmatic control with access to state properties and methods likesetOpen(). Never use both approaches together - choose one based on your needs.
Nested Dialogs
Multiple dialogs can be stacked with automatic z-index management. Zag.js manages layering through CSS variables like
--z-index and --layer-index, which are automatically updated when dialogs are opened or closed:
Example not foundExample not foundExample not foundExample not foundConfirmation Dialog
Dialogs can intercept close attempts to show confirmation prompts. This pattern is useful for preventing data loss from unsaved changes:
Example not foundExample not foundExample not foundExample not foundGuides
Nested Dialog Styling
You can create a zoom-out effect for parent dialogs using the data-has-nested attribute and --nested-layer-count
variable:
[data-scope='dialog'][data-part='backdrop'][data-has-nested] {
transform: scale(calc(1 - var(--nested-layer-count) * 0.05));
}
Lazy Mount and Dynamic Imports
When using lazyMount and dynamically rendering components in the dialog (via React.lazy, Next.js dynamic), wrap
the imported component in a Suspense component to render a fallback.
import { Dialog } from '@ark-ui/react/dialog'
import { Suspense } from 'react'
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'))
export default function DialogExample() {
return (
<Dialog.Root lazyMount>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Dialog.Content>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</Dialog.Content>
</Dialog.Root>
)
}
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
aria-label | stringHuman readable label for the dialog, in event the dialog title is not rendered | |
closeOnEscape | true | booleanWhether to close the dialog when the escape key is pressed |
closeOnInteractOutside | true | booleanWhether to close the dialog when the outside is clicked |
defaultOpen | false | booleanThe initial open state of the dialog when rendered. Use when you don't need to control the open state of the dialog. |
finalFocusEl | () => MaybeElementElement to receive focus when the dialog is closed | |
id | stringThe unique identifier of the machine. | |
ids | Partial<{
trigger: string
positioner: string
backdrop: string
content: string
closeTrigger: string
title: string
description: string
}>The ids of the elements in the dialog. Useful for composition. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
initialFocusEl | () => MaybeElementElement to receive focus when the dialog is opened | |
lazyMount | false | booleanWhether to enable lazy mounting |
modal | true | booleanWhether to prevent pointer interaction outside the element and hide all content below it |
onEscapeKeyDown | (event: KeyboardEvent) => voidFunction called when the escape key is pressed | |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
onFocusOutside | (event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the component | |
onInteractOutside | (event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component | |
onOpenChange | (details: OpenChangeDetails) => voidFunction to call when the dialog's open state changes | |
onPointerDownOutside | (event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the component | |
onRequestDismiss | (event: LayerDismissEvent) => voidFunction called when this layer is closed due to a parent layer being closed | |
open | booleanThe controlled open state of the dialog | |
persistentElements | (() => Element | null)[]Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event | |
present | booleanWhether the node is present (controlled by the user) | |
preventScroll | true | booleanWhether to prevent scrolling behind the dialog when it's opened |
restoreFocus | booleanWhether to restore focus to the element that had focus before the dialog was opened | |
role | 'dialog' | 'dialog' | 'alertdialog'The dialog's role |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
trapFocus | true | booleanWhether to trap focus inside the dialog when it's opened |
unmountOnExit | false | booleanWhether to unmount on exit. |
Backdrop
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| CSS Variable | Description |
|---|---|
--layer-index | The index of the dismissable in the layer stack |
| Data Attribute | Value |
|---|---|
[data-scope] | dialog |
[data-part] | backdrop |
[data-state] | "open" | "closed" |
CloseTrigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Content
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| CSS Variable | Description |
|---|---|
--layer-index | The index of the dismissable in the layer stack |
--nested-layer-count | The number of nested dialogs |
| Data Attribute | Value |
|---|---|
[data-scope] | dialog |
[data-part] | content |
[data-state] | "open" | "closed" |
[data-nested] | dialog |
[data-has-nested] | dialog |
Description
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Positioner
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseDialogReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
immediate | booleanWhether to synchronize the present change immediately or defer it to the next frame | |
lazyMount | false | booleanWhether to enable lazy mounting |
onExitComplete | VoidFunctionFunction called when the animation ends in the closed state | |
present | booleanWhether the node is present (controlled by the user) | |
skipAnimationOnMount | false | booleanWhether to allow the initial presence animation. |
unmountOnExit | false | booleanWhether to unmount on exit. |
Title
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Trigger
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | dialog |
[data-part] | trigger |
[data-state] | "open" | "closed" |
Context
These are the properties available when using Dialog.Context, useDialogContext hook or useDialog hook.
API
| Property | Type |
|---|---|
open | booleanWhether the dialog is open |
setOpen | (open: boolean) => voidFunction to open or close the dialog |
Accessibility
Complies with the Dialog WAI-ARIA design pattern.
Keyboard Support
| Key | Description |
|---|---|
Enter | When focus is on the trigger, opens the dialog. |
Tab | Moves focus to the next focusable element within the content. Focus is trapped within the dialog. |
Shift + Tab | Moves focus to the previous focusable element. Focus is trapped within the dialog. |
Esc | Closes the dialog and moves focus to trigger or the defined final focus element |