Components
Dialog

Dialog

A modal window that appears on top of the main content.

You can explore the dialog component in the following curated examples.

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-part attribute 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

import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'

export const Basic = () => (
  <Dialog.Root>
    <Dialog.Trigger>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop />
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>Dialog Title</Dialog.Title>
          <Dialog.Description>Dialog Description</Dialog.Description>
          <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

Controlled Dialog

To create a controlled Dialog component, manage the state of the dialog using the open and onOpenChange props:

import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { useState } from 'react'

export const Controlled = () => {
  const [isOpen, setIsOpen] = useState(false)
  return (
    <>
      <button type="button" onClick={() => setIsOpen(true)}>
        Open Dialog
      </button>
      <Dialog.Root open={isOpen} onOpenChange={(e) => setIsOpen(e.open)}>
        <Portal>
          <Dialog.Backdrop />
          <Dialog.Positioner>
            <Dialog.Content>
              <Dialog.Title>Dialog Title</Dialog.Title>
              <Dialog.Description>Dialog Description</Dialog.Description>
              <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.Root>
    </>
  )
}

Lazy Mounting

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.

import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'

export const LazyMount = () => (
  <Dialog.Root lazyMount unmountOnExit onExitComplete={() => console.log('onExitComplete invoked')}>
    <Dialog.Trigger>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop />
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>Dialog Title</Dialog.Title>
          <Dialog.Description>Dialog Description</Dialog.Description>
          <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

Using Render Function

The Dialog component supports the use of a render function as a child for more control. This allows access to dialog states like isOpen:

import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'

export const RenderFn = () => (
  <Dialog.Root>
    <Dialog.Trigger>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop />
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>Dialog Title</Dialog.Title>
          <Dialog.Description>Dialog Description</Dialog.Description>
          <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
    <Dialog.Context>
      {(dialog) => <p>Dialog is {dialog.open ? 'open' : 'closed'}</p>}
    </Dialog.Context>
  </Dialog.Root>
)

Using the Root Provider

The RootProvider component provides a context for the dialog. It accepts the value of the useDialog hook. You can leverage it to access the component state and methods from outside the dialog.

import { Dialog, useDialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'

export const RootProvider = () => {
  const dialog = useDialog()

  return (
    <>
      <button onClick={() => dialog.setOpen(true)}>Open</button>

      <Dialog.RootProvider value={dialog}>
        <Dialog.Trigger>Open Dialog</Dialog.Trigger>
        <Portal>
          <Dialog.Backdrop />
          <Dialog.Positioner>
            <Dialog.Content>
              <Dialog.Title>Dialog Title</Dialog.Title>
              <Dialog.Description>Dialog Description</Dialog.Description>
              <Dialog.CloseTrigger>Close</Dialog.CloseTrigger>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

If you're using the RootProvider component, you don't need to use the Root component.

API Reference

Root

PropDefaultType
aria-label
string

Human readable label for the dialog, in event the dialog title is not rendered

closeOnEscapetrue
boolean

Whether to close the dialog when the escape key is pressed

closeOnInteractOutsidetrue
boolean

Whether to close the dialog when the outside is clicked

defaultOpen
boolean

The initial open state of the dialog when it is first rendered. Use when you do not need to control its open state.

finalFocusEl
() => HTMLElement | null

Element to receive focus when the dialog is closed

id
string

The 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
boolean

Whether to synchronize the present change immediately or defer it to the next frame

initialFocusEl
() => HTMLElement | null

Element to receive focus when the dialog is opened

lazyMountfalse
boolean

Whether to enable lazy mounting

modaltrue
boolean

Whether to prevent pointer interaction outside the element and hide all content below it

onEscapeKeyDown
(event: KeyboardEvent) => void

Function called when the escape key is pressed

onExitComplete
() => void

Function called when the animation ends in the closed state

onFocusOutside
(event: FocusOutsideEvent) => void

Function called when the focus is moved outside the component

onInteractOutside
(event: InteractOutsideEvent) => void

Function called when an interaction happens outside the component

onOpenChange
(details: OpenChangeDetails) => void

Callback to be invoked when the dialog is opened or closed

onPointerDownOutside
(event: PointerDownOutsideEvent) => void

Function called when the pointer is pressed down outside the component

open
boolean

Whether the dialog is open

persistentElements
(() => Element | null)[]

Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event

present
boolean

Whether the node is present (controlled by the user)

preventScrolltrue
boolean

Whether to prevent scrolling behind the dialog when it's opened

restoreFocus
boolean

Whether to restore focus to the element that had focus before the dialog was opened

role'dialog'
'dialog' | 'alertdialog'

The dialog's role

trapFocustrue
boolean

Whether to trap focus inside the dialog when it's opened

unmountOnExitfalse
boolean

Whether to unmount on exit.

Backdrop

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]dialog
[data-part]backdrop
[data-state]"open" | "closed"

CloseTrigger

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Content

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]dialog
[data-part]content
[data-state]"open" | "closed"

Description

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Positioner

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

RootProvider

PropDefaultType
value
UseDialogReturn

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
immediate
boolean

Whether to synchronize the present change immediately or defer it to the next frame

lazyMountfalse
boolean

Whether to enable lazy mounting

onExitComplete
() => void

Function called when the animation ends in the closed state

present
boolean

Whether the node is present (controlled by the user)

unmountOnExitfalse
boolean

Whether to unmount on exit.

Title

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Trigger

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]dialog
[data-part]trigger
[data-state]"open" | "closed"

Accessibility

Complies with the Dialog WAI-ARIA design pattern.

Keyboard Support

KeyDescription
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