Dialog
A modal window that appears on top of the main content.
Dialog Title
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
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>
)
import { Dialog } from '@ark-ui/solid/dialog'
import { Portal } from 'solid-js/web'
export const Basic = () => {
return (
<Dialog.Root defaultOpen>
<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>
)
}
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
</script>
<template>
<Dialog.Root>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Teleport to="body">
<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>
</Teleport>
</Dialog.Root>
</template>
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>
</>
)
}
import { Dialog } from '@ark-ui/solid/dialog'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
export const Controlled = () => {
const [open, setOpen] = createSignal(false)
return (
<>
<button type="button" onClick={() => setOpen(true)}>
Open Dialog
</button>
<Dialog.Root open={open()} onOpenChange={() => setOpen(false)}>
<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>
</>
)
}
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="() => (open = true)">{{ open ? 'Close' : 'Open' }} Dialog</button>
<Dialog.Root v-model:open="open">
<Teleport to="body">
<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>
</Teleport>
</Dialog.Root>
</template>
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>
)
import { Dialog } from '@ark-ui/solid/dialog'
export const LazyMount = () => {
return (
<Dialog.Root lazyMount unmountOnExit>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<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>
</Dialog.Root>
)
}
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
</script>
<template>
<Dialog.Root lazyMount unmountOnExit>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<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>
</Dialog.Root>
</template>
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>
)
import { Dialog } from '@ark-ui/solid/dialog'
import { Portal } from 'solid-js/web'
export const RenderFn = () => {
return (
<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>{(context) => <p>Dialog is {context().open ? 'open' : 'closed'}</p>}</Dialog.Context>
</Dialog.Root>
)
}
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
</script>
<template>
<Dialog.Root>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Teleport to="body">
<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>
</Teleport>
<Dialog.Context v-slot="dialog">
<p>Dialog is {{ dialog.open ? 'open' : 'closed' }}</p>
</Dialog.Context>
</Dialog.Root>
</template>
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>
</>
)
}
import { Dialog, useDialog } from '@ark-ui/solid/dialog'
import { Portal } from 'solid-js/web'
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>
</>
)
}
<script setup lang="ts">
import { Dialog, useDialog } from '@ark-ui/vue/dialog'
const dialog = useDialog()
</script>
<template>
<button @click="dialog.setOpen(true)">Open</button>
<Dialog.RootProvider :value="dialog">
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Teleport to="body">
<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>
</Teleport>
</Dialog.RootProvider>
</template>
If you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
API Reference
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 | |
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 |
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. |
| 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. |
| Data Attribute | Value |
|---|---|
[data-scope] | dialog |
[data-part] | content |
[data-state] | "open" | "closed" |
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) | |
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" |
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 |