# Dialog

URL: https://ark-ui.com/docs/components/dialog
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/dialog.mdx

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

---

<ComponentPreview id="Dialog" />

## 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.

<Anatomy id="dialog" />

## Examples

Learn how to use the `Dialog` component in your project. Let's take a look at the most basic example

**Example: basic**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Basic = () => (
  <Dialog.Root>
    <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title className={styles.Title}>Welcome Back</Dialog.Title>
          <Dialog.Description className={styles.Description}>Sign in to your account to continue.</Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Basic = () => (
  <Dialog.Root>
    <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Welcome Back</Dialog.Title>
          <Dialog.Description class={styles.Description}>Sign in to your account to continue.</Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Welcome Back</Dialog.Title>
          <Dialog.Description :class="styles.Description">Sign in to your account to continue.</Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'
</script>

<Dialog.Root>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Welcome Back</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          Sign in to your account to continue.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Controlled

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

**Example: controlled**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Controlled = () => {
  const [open, setOpen] = useState(false)
  return (
    <Dialog.Root open={open} onOpenChange={(e) => setOpen(e.open)}>
      <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
      <Portal>
        <Dialog.Backdrop className={styles.Backdrop} />
        <Dialog.Positioner className={styles.Positioner}>
          <Dialog.Content className={styles.Content}>
            <Dialog.CloseTrigger className={styles.CloseTrigger}>
              <XIcon />
            </Dialog.CloseTrigger>
            <Dialog.Title className={styles.Title}>Session Settings</Dialog.Title>
            <Dialog.Description className={styles.Description}>
              Manage your session preferences and security options.
            </Dialog.Description>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Controlled = () => {
  const [open, setOpen] = createSignal(false)

  return (
    <Dialog.Root open={open()} onOpenChange={(e) => setOpen(e.open)}>
      <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
      <Portal>
        <Dialog.Backdrop class={styles.Backdrop} />
        <Dialog.Positioner class={styles.Positioner}>
          <Dialog.Content class={styles.Content}>
            <Dialog.CloseTrigger class={styles.CloseTrigger}>
              <XIcon />
            </Dialog.CloseTrigger>
            <Dialog.Title class={styles.Title}>Session Settings</Dialog.Title>
            <Dialog.Description class={styles.Description}>
              Manage your session preferences and security options.
            </Dialog.Description>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const open = ref(false)
</script>

<template>
  <button :class="button.Root" @click="() => (open = true)">Toggle</button>
  <Dialog.Root v-model:open="open">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Session Settings</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            Manage your session preferences and security options.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  let open = $state(false)
</script>

<button type="button" class={button.Root} onclick={() => (open = !open)}>Toggle</button>
<Dialog.Root bind:open>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Session Settings</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          Manage your session preferences and security options.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Lazy 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: lazy-mount**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const LazyMount = () => (
  <Dialog.Root lazyMount unmountOnExit>
    <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title className={styles.Title}>Lazy Loaded</Dialog.Title>
          <Dialog.Description className={styles.Description}>
            This dialog content is only mounted when opened and unmounts on close.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const LazyMount = () => (
  <Dialog.Root lazyMount unmountOnExit>
    <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Lazy Mounted Dialog</Dialog.Title>
          <Dialog.Description class={styles.Description}>
            This dialog content is only mounted when opened and unmounted when closed.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
</script>

<template>
  <Dialog.Root lazyMount unmountOnExit>
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Lazy Mounted Dialog</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This dialog content is only mounted when opened and unmounted when closed.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'
</script>

<Dialog.Root lazyMount unmountOnExit>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Lazy Mounted Dialog</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This dialog content is only mounted when opened and unmounted when closed.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Alert 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: alert-dialog**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const AlertDialog = () => (
  <Dialog.Root role="alertdialog">
    <Dialog.Trigger className={button.Root}>Delete Account</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.Title className={styles.Title}>Are you absolutely sure?</Dialog.Title>
          <Dialog.Description className={styles.Description}>
            This action cannot be undone. This will permanently delete your account and remove your data from our
            servers.
          </Dialog.Description>
          <div className={styles.Actions}>
            <Dialog.CloseTrigger className={button.Root}>Cancel</Dialog.CloseTrigger>
            <button className={button.Root} data-variant="solid">
              Delete Account
            </button>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const AlertDialog = () => (
  <Dialog.Root role="alertdialog">
    <Dialog.Trigger class={button.Root}>Delete Account</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Are you absolutely sure?</Dialog.Title>
          <Dialog.Description class={styles.Description}>
            This action cannot be undone. This will permanently delete your account and remove your data from our
            servers.
          </Dialog.Description>
          <div class={styles.Actions}>
            <Dialog.CloseTrigger class={button.Root}>Cancel</Dialog.CloseTrigger>
            <button type="button" class={button.Root}>
              Delete Account
            </button>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
</script>

<template>
  <Dialog.Root role="alertdialog">
    <Dialog.Trigger :class="button.Root">Delete Account</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Are you absolutely sure?</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This action cannot be undone. This will permanently delete your account and remove your data from our
            servers.
          </Dialog.Description>
          <div :class="styles.Actions">
            <Dialog.CloseTrigger :class="button.Root">Cancel</Dialog.CloseTrigger>
            <button type="button" :class="button.Root">Delete Account</button>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'
</script>

<Dialog.Root role="alertdialog">
  <Dialog.Trigger class={button.Root}>Delete Account</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Are you absolutely sure?</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This action cannot be undone. This will permanently delete your account and remove your
          data from our servers.
        </Dialog.Description>
        <div class={styles.Actions}>
          <Dialog.CloseTrigger class={button.Root}>Cancel</Dialog.CloseTrigger>
          <button type="button" class={button.Root}>Delete Account</button>
        </div>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Initial 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: initial-focus**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import { useRef } from 'react'
import button from 'styles/button.module.css'
import field from 'styles/field.module.css'
import styles from 'styles/dialog.module.css'

export const InitialFocus = () => {
  const inputRef = useRef<HTMLInputElement>(null)

  return (
    <Dialog.Root initialFocusEl={() => inputRef.current}>
      <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
      <Portal>
        <Dialog.Backdrop className={styles.Backdrop} />
        <Dialog.Positioner className={styles.Positioner}>
          <Dialog.Content className={styles.Content}>
            <Dialog.CloseTrigger className={styles.CloseTrigger}>
              <XIcon />
            </Dialog.CloseTrigger>
            <Dialog.Title className={styles.Title}>Edit Profile</Dialog.Title>
            <Dialog.Description className={styles.Description}>
              The first input will be focused when the dialog opens.
            </Dialog.Description>
            <div className={styles.Body}>
              <input ref={inputRef} className={field.Input} placeholder="Enter your name..." />
              <input className={field.Input} placeholder="Enter your email..." />
            </div>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const InitialFocus = () => {
  let inputRef: HTMLInputElement | undefined

  return (
    <Dialog.Root initialFocusEl={() => inputRef}>
      <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
      <Portal>
        <Dialog.Backdrop class={styles.Backdrop} />
        <Dialog.Positioner class={styles.Positioner}>
          <Dialog.Content class={styles.Content}>
            <Dialog.CloseTrigger class={styles.CloseTrigger}>
              <XIcon />
            </Dialog.CloseTrigger>
            <Dialog.Title class={styles.Title}>Edit Profile</Dialog.Title>
            <Dialog.Description class={styles.Description}>
              The name input will be focused when this dialog opens.
            </Dialog.Description>
            <div class={styles.Body}>
              <input ref={inputRef} type="text" placeholder="Enter your name" />
              <input type="email" placeholder="Enter your email" />
            </div>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const inputRef = ref<HTMLInputElement | null>(null)
</script>

<template>
  <Dialog.Root :initial-focus-el="() => inputRef">
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Edit Profile</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            The name input will be focused when this dialog opens.
          </Dialog.Description>
          <div :class="styles.Body">
            <input ref="inputRef" type="text" placeholder="Enter your name" />
            <input type="email" placeholder="Enter your email" />
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  let inputRef: HTMLInputElement
</script>

<Dialog.Root initialFocusEl={() => inputRef}>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Edit Profile</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          The name input will be focused when this dialog opens.
        </Dialog.Description>
        <div class={styles.Body}>
          <input bind:this={inputRef} type="text" placeholder="Enter your name" />
          <input type="email" placeholder="Enter your email" />
        </div>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Final 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: final-focus**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import { useRef } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const FinalFocus = () => {
  const finalRef = useRef<HTMLButtonElement>(null)

  return (
    <div className="stack">
      <button className={button.Root} ref={finalRef}>
        I will receive focus when dialog closes
      </button>
      <Dialog.Root finalFocusEl={() => finalRef.current}>
        <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Focus Redirect</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                When this dialog closes, focus will return to the button above instead of the trigger.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.Root>
    </div>
  )
}

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const FinalFocus = () => {
  let buttonRef: HTMLButtonElement | undefined

  return (
    <>
      <button ref={buttonRef} class={button.Root}>
        Focus Target
      </button>
      <Dialog.Root finalFocusEl={() => buttonRef}>
        <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.CloseTrigger class={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title class={styles.Title}>Custom Focus Return</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                When this dialog closes, focus will return to the "Focus Target" button instead of the trigger.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.Root>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const buttonRef = ref<HTMLButtonElement | null>(null)
</script>

<template>
  <button ref="buttonRef" :class="button.Root">Focus Target</button>
  <Dialog.Root :final-focus-el="() => buttonRef">
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Custom Focus Return</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            When this dialog closes, focus will return to the "Focus Target" button instead of the trigger.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  let buttonRef: HTMLButtonElement
</script>

<button bind:this={buttonRef} class={button.Root}>Focus Target</button>
<Dialog.Root finalFocusEl={() => buttonRef}>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Custom Focus Return</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          When this dialog closes, focus will return to the "Focus Target" button instead of the
          trigger.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Non-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: non-modal**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const NonModal = () => (
  <Dialog.Root modal={false}>
    <Dialog.Trigger className={button.Root}>Open Non-Modal Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title className={styles.Title}>Non-Modal Dialog</Dialog.Title>
          <Dialog.Description className={styles.Description}>
            This dialog allows interaction with elements outside. You can click buttons, select text, and interact with
            the page behind it.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const NonModal = () => (
  <Dialog.Root modal={false}>
    <Dialog.Trigger class={button.Root}>Open Non-Modal</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Non-Modal Dialog</Dialog.Title>
          <Dialog.Description class={styles.Description}>
            This dialog allows interaction with elements outside while open.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
</script>

<template>
  <Dialog.Root :modal="false">
    <Dialog.Trigger :class="button.Root">Open Non-Modal</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Non-Modal Dialog</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This dialog allows interaction with elements outside while open.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'
</script>

<Dialog.Root modal={false}>
  <Dialog.Trigger class={button.Root}>Open Non-Modal</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Non-Modal Dialog</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This dialog allows interaction with elements outside while open.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Close 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: close-on-interact-outside**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const CloseOnInteractOutside = () => (
  <Dialog.Root closeOnInteractOutside={false}>
    <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title className={styles.Title}>Custom Close Behavior</Dialog.Title>
          <Dialog.Description className={styles.Description}>
            This dialog will not close when clicking outside. Only the close button will dismiss it.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const CloseOnInteractOutside = () => (
  <Dialog.Root closeOnInteractOutside={false}>
    <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Click Outside Disabled</Dialog.Title>
          <Dialog.Description class={styles.Description}>
            Clicking outside this dialog will not close it. Use the close button instead.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'
</script>

<template>
  <Dialog.Root :close-on-interact-outside="false">
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Click Outside Disabled</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            Clicking outside this dialog will not close it. Use the close button instead.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'
</script>

<Dialog.Root closeOnInteractOutside={false}>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Click Outside Disabled</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          Clicking outside this dialog will not close it. Use the close button instead.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Close 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: close-on-escape**

#### React

```tsx
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import field from 'styles/field.module.css'
import styles from 'styles/dialog.module.css'

export const CloseOnEscape = () => (
  <Dialog.Root closeOnEscape={false}>
    <Dialog.Trigger className={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop className={styles.Backdrop} />
      <Dialog.Positioner className={styles.Positioner}>
        <Dialog.Content className={styles.Content}>
          <Dialog.CloseTrigger className={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title className={styles.Title}>Unsaved Changes</Dialog.Title>
          <Dialog.Description className={styles.Description}>
            This dialog prevents closing with the Escape key. Use the close button to dismiss.
          </Dialog.Description>
          <div className={styles.Body}>
            <textarea className={field.Textarea} placeholder="Type something..." rows={3} />
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Solid

```tsx
import { Dialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const CloseOnEscape = () => (
  <Dialog.Root
    closeOnEscape={false}
    onEscapeKeyDown={(e) => {
      e.preventDefault()
      alert('Escape key pressed but dialog stays open')
    }}
  >
    <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
    <Portal>
      <Dialog.Backdrop class={styles.Backdrop} />
      <Dialog.Positioner class={styles.Positioner}>
        <Dialog.Content class={styles.Content}>
          <Dialog.CloseTrigger class={styles.CloseTrigger}>
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title class={styles.Title}>Escape Disabled</Dialog.Title>
          <Dialog.Description class={styles.Description}>
            This dialog will not close when pressing the Escape key. Use the close button instead.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
)

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const handleEscapeKeyDown = (e: KeyboardEvent) => {
  e.preventDefault()
  alert('Escape key pressed but dialog stays open')
}
</script>

<template>
  <Dialog.Root :close-on-escape="false" :on-escape-key-down="handleEscapeKeyDown">
    <Dialog.Trigger :class="button.Root">Open Dialog</Dialog.Trigger>
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Escape Disabled</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This dialog will not close when pressing the Escape key. Use the close button instead.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  const handleEscapeKeyDown = (e: KeyboardEvent) => {
    e.preventDefault()
    alert('Escape key pressed but dialog stays open')
  }
</script>

<Dialog.Root closeOnEscape={false} onEscapeKeyDown={handleEscapeKeyDown}>
  <Dialog.Trigger class={button.Root}>Open Dialog</Dialog.Trigger>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Escape Disabled</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This dialog will not close when pressing the Escape key. Use the close button instead.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.Root>

```

### Render Function

Use the `Dialog.Context` component to access the dialog's state and methods.

<Example id="render-fn" />

### Root 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: root-provider**

#### React

```tsx
import { Dialog, useDialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

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

  return (
    <div className="stack">
      <button className={button.Root} onClick={() => dialog.setOpen(true)}>
        Dialog is {dialog.open ? 'open' : 'closed'}
      </button>
      <Dialog.RootProvider value={dialog}>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Controlled Externally</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                This dialog is controlled via the useDialog hook.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </div>
  )
}

```

#### Solid

```tsx
import { Dialog, useDialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

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

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

      <Dialog.RootProvider value={dialog}>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.CloseTrigger class={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title class={styles.Title}>Account Settings</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                Manage your account preferences and security options.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog, useDialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const dialog = useDialog()
</script>

<template>
  <button :class="button.Root" @click="dialog.setOpen(true)">Open</button>

  <Dialog.RootProvider :value="dialog">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Account Settings</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            Manage your account preferences and security options.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog, useDialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  const id = $props.id()

  const dialog = useDialog({ id })
</script>

<button class={button.Root} onclick={() => dialog().setOpen(true)}>Open</button>

<Dialog.RootProvider value={dialog}>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Account Settings</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          Manage your account preferences and security options.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.RootProvider>

```

> **Note:** There are two ways to use the Dialog component: (1) `Dialog.Root` for declarative usage, or (2)
> `useDialog()` + `Dialog.RootProvider` for programmatic control with access to state properties and methods like
> `setOpen()`. 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: nested**

#### React

```tsx
import { Dialog, useDialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Nested = () => {
  const parentDialog = useDialog()
  const childDialog = useDialog()

  return (
    <>
      <button className={button.Root} onClick={() => parentDialog.setOpen(true)}>
        Open Parent Dialog
      </button>

      <Dialog.RootProvider value={parentDialog}>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Parent Dialog</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                This is the parent dialog. Open a nested dialog to see automatic z-index management.
              </Dialog.Description>
              <div className={styles.Body}>
                <button className={button.Root} onClick={() => childDialog.setOpen(true)}>
                  Open Nested Dialog
                </button>
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>

      <Dialog.RootProvider value={childDialog}>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Nested Dialog</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                This dialog is nested within the parent with proper z-index layering.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

```

#### Solid

```tsx
import { Dialog, useDialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Nested = () => {
  const parentDialog = useDialog()
  const childDialog = useDialog()

  return (
    <>
      <button class={button.Root} onClick={() => parentDialog().setOpen(true)}>
        Open Parent Dialog
      </button>

      <Dialog.RootProvider value={parentDialog}>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.CloseTrigger class={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title class={styles.Title}>Parent Dialog</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                This is the parent dialog. Open a nested dialog from here.
              </Dialog.Description>
              <div class={styles.Actions}>
                <button class={button.Root} onClick={() => childDialog().setOpen(true)}>
                  Open Nested
                </button>
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>

      <Dialog.RootProvider value={childDialog}>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.CloseTrigger class={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title class={styles.Title}>Nested Dialog</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                This is a nested dialog with proper z-index layering.
              </Dialog.Description>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog, useDialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const parentDialog = useDialog()
const childDialog = useDialog()
</script>

<template>
  <button :class="button.Root" @click="parentDialog.setOpen(true)">Open Parent Dialog</button>

  <Dialog.RootProvider :value="parentDialog">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Parent Dialog</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This is the parent dialog. Open a nested dialog from here.
          </Dialog.Description>
          <div :class="styles.Actions">
            <button :class="button.Root" @click="childDialog.setOpen(true)">Open Nested</button>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.RootProvider>

  <Dialog.RootProvider :value="childDialog">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Nested Dialog</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            This is a nested dialog with proper z-index layering.
          </Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog, useDialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  const id = $props.id()

  const parentDialog = useDialog({ id: `${id}-parent` })
  const childDialog = useDialog({ id: `${id}-child` })
</script>

<button class={button.Root} onclick={() => parentDialog().setOpen(true)}>Open Parent Dialog</button>

<Dialog.RootProvider value={parentDialog}>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Parent Dialog</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This is the parent dialog. Open a nested dialog from here.
        </Dialog.Description>
        <div class={styles.Actions}>
          <button class={button.Root} onclick={() => childDialog().setOpen(true)}>Open Nested</button>
        </div>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.RootProvider>

<Dialog.RootProvider value={childDialog}>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Nested Dialog</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          This is a nested dialog with proper z-index layering.
        </Dialog.Description>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.RootProvider>

```

### Confirmation Dialog

Dialogs can intercept close attempts to show confirmation prompts. This pattern is useful for preventing data loss from
unsaved changes:

**Example: confirmation**

#### React

```tsx
import { Dialog, useDialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
import { XIcon } from 'lucide-react'
import { useState } from 'react'
import button from 'styles/button.module.css'
import field from 'styles/field.module.css'
import styles from 'styles/dialog.module.css'

export const Confirmation = () => {
  const [formContent, setFormContent] = useState('')
  const [isParentDialogOpen, setIsParentDialogOpen] = useState(false)

  const parentDialog = useDialog({
    open: isParentDialogOpen,
    onOpenChange: (details) => {
      if (!details.open && formContent.trim()) {
        confirmDialog.setOpen(true)
      } else {
        setIsParentDialogOpen(details.open)
      }
    },
  })

  const confirmDialog = useDialog()

  const handleConfirmClose = () => {
    confirmDialog.setOpen(false)
    parentDialog.setOpen(false)
    setFormContent('')
  }

  return (
    <>
      <button className={button.Root} onClick={() => parentDialog.setOpen(true)}>
        Open Form
      </button>

      <Dialog.RootProvider value={parentDialog}>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Edit Content</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                Make changes to your content. You'll be asked to confirm before closing if there are unsaved changes.
              </Dialog.Description>
              <div className={styles.Body}>
                <textarea
                  className={field.Textarea}
                  value={formContent}
                  onChange={(e) => setFormContent(e.target.value)}
                  placeholder="Enter some text..."
                  rows={4}
                />
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>

      <Dialog.RootProvider value={confirmDialog}>
        <Portal>
          <Dialog.Backdrop className={styles.Backdrop} />
          <Dialog.Positioner className={styles.Positioner}>
            <Dialog.Content className={styles.Content}>
              <Dialog.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title className={styles.Title}>Unsaved Changes</Dialog.Title>
              <Dialog.Description className={styles.Description}>
                You have unsaved changes. Are you sure you want to close without saving?
              </Dialog.Description>
              <div className={styles.Actions}>
                <button className={button.Root} onClick={() => confirmDialog.setOpen(false)}>
                  Keep Editing
                </button>
                <button className={button.Root} data-variant="solid" onClick={handleConfirmClose}>
                  Discard Changes
                </button>
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

```

#### Solid

```tsx
import { Dialog, useDialog } from '@ark-ui/solid/dialog'
import { XIcon } from 'lucide-solid'
import { createSignal } from 'solid-js'
import { Portal } from 'solid-js/web'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

export const Confirmation = () => {
  const [formContent, setFormContent] = createSignal('')
  const parentDialog = useDialog({
    onOpenChange: (details) => {
      if (!details.open && formContent().trim()) {
        confirmDialog().setOpen(true)
      }
    },
  })
  const confirmDialog = useDialog()

  const handleConfirmClose = () => {
    confirmDialog().setOpen(false)
    parentDialog().setOpen(false)
    setFormContent('')
  }

  return (
    <>
      <button class={button.Root} onClick={() => parentDialog().setOpen(true)}>
        Open Form
      </button>

      <Dialog.RootProvider value={parentDialog}>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.CloseTrigger class={styles.CloseTrigger}>
                <XIcon />
              </Dialog.CloseTrigger>
              <Dialog.Title class={styles.Title}>Edit Content</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                Make changes to your content. You'll be asked to confirm if you have unsaved changes.
              </Dialog.Description>
              <div class={styles.Body}>
                <textarea
                  value={formContent()}
                  onInput={(e) => setFormContent(e.currentTarget.value)}
                  placeholder="Enter some text..."
                  rows={4}
                />
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>

      <Dialog.RootProvider value={confirmDialog}>
        <Portal>
          <Dialog.Backdrop class={styles.Backdrop} />
          <Dialog.Positioner class={styles.Positioner}>
            <Dialog.Content class={styles.Content}>
              <Dialog.Title class={styles.Title}>Unsaved Changes</Dialog.Title>
              <Dialog.Description class={styles.Description}>
                You have unsaved changes. Are you sure you want to discard them?
              </Dialog.Description>
              <div class={styles.Actions}>
                <button class={button.Root} onClick={() => confirmDialog().setOpen(false)}>
                  Keep Editing
                </button>
                <button class={button.Root} onClick={handleConfirmClose}>
                  Discard
                </button>
              </div>
            </Dialog.Content>
          </Dialog.Positioner>
        </Portal>
      </Dialog.RootProvider>
    </>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { Dialog, useDialog } from '@ark-ui/vue/dialog'
import { XIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import button from 'styles/button.module.css'
import styles from 'styles/dialog.module.css'

const formContent = ref('')

const confirmDialog = useDialog()
const parentDialog = useDialog({
  onOpenChange: (details) => {
    if (!details.open && formContent.value.trim()) {
      confirmDialog.value.setOpen(true)
    }
  },
})

const handleConfirmClose = () => {
  confirmDialog.value.setOpen(false)
  parentDialog.value.setOpen(false)
  formContent.value = ''
}
</script>

<template>
  <button :class="button.Root" @click="parentDialog.setOpen(true)">Open Form</button>

  <Dialog.RootProvider :value="parentDialog">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.CloseTrigger :class="styles.CloseTrigger">
            <XIcon />
          </Dialog.CloseTrigger>
          <Dialog.Title :class="styles.Title">Edit Content</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            Make changes to your content. You'll be asked to confirm if you have unsaved changes.
          </Dialog.Description>
          <div :class="styles.Body">
            <textarea v-model="formContent" placeholder="Enter some text..." rows="4"></textarea>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.RootProvider>

  <Dialog.RootProvider :value="confirmDialog">
    <Teleport to="body">
      <Dialog.Backdrop :class="styles.Backdrop" />
      <Dialog.Positioner :class="styles.Positioner">
        <Dialog.Content :class="styles.Content">
          <Dialog.Title :class="styles.Title">Unsaved Changes</Dialog.Title>
          <Dialog.Description :class="styles.Description">
            You have unsaved changes. Are you sure you want to discard them?
          </Dialog.Description>
          <div :class="styles.Actions">
            <button :class="button.Root" @click="confirmDialog.setOpen(false)">Keep Editing</button>
            <button :class="button.Root" @click="handleConfirmClose">Discard</button>
          </div>
        </Dialog.Content>
      </Dialog.Positioner>
    </Teleport>
  </Dialog.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { Dialog, useDialog } from '@ark-ui/svelte/dialog'
  import { Portal } from '@ark-ui/svelte/portal'
  import { XIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/dialog.module.css'

  const id = $props.id()
  let formContent = $state('')

  const confirmDialog = useDialog({ id: `${id}-confirm` })
  const parentDialog = useDialog({
    id: `${id}-parent`,
    onOpenChange: (details) => {
      if (!details.open && formContent.trim()) {
        confirmDialog().setOpen(true)
      }
    },
  })

  const handleConfirmClose = () => {
    confirmDialog().setOpen(false)
    parentDialog().setOpen(false)
    formContent = ''
  }
</script>

<button class={button.Root} onclick={() => parentDialog().setOpen(true)}>Open Form</button>

<Dialog.RootProvider value={parentDialog}>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.CloseTrigger class={styles.CloseTrigger}>
          <XIcon />
        </Dialog.CloseTrigger>
        <Dialog.Title class={styles.Title}>Edit Content</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          Make changes to your content. You'll be asked to confirm if you have unsaved changes.
        </Dialog.Description>
        <div class={styles.Body}>
          <textarea bind:value={formContent} placeholder="Enter some text..." rows={4}></textarea>
        </div>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.RootProvider>

<Dialog.RootProvider value={confirmDialog}>
  <Portal>
    <Dialog.Backdrop class={styles.Backdrop} />
    <Dialog.Positioner class={styles.Positioner}>
      <Dialog.Content class={styles.Content}>
        <Dialog.Title class={styles.Title}>Unsaved Changes</Dialog.Title>
        <Dialog.Description class={styles.Description}>
          You have unsaved changes. Are you sure you want to discard them?
        </Dialog.Description>
        <div class={styles.Actions}>
          <button class={button.Root} onclick={() => confirmDialog().setOpen(false)}>Keep Editing</button>
          <button class={button.Root} onclick={handleConfirmClose}>Discard</button>
        </div>
      </Dialog.Content>
    </Dialog.Positioner>
  </Portal>
</Dialog.RootProvider>

```

## Guides

### Nested Dialog Styling

You can create a zoom-out effect for parent dialogs using the `data-has-nested` attribute and `--nested-layer-count`
variable:

```css
[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.

```tsx
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

**Component API Reference**

#### React

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `aria-label` | `string` | No | Human readable label for the dialog, in event the dialog title is not rendered |
| `closeOnEscape` | `boolean` | No | Whether to close the dialog when the escape key is pressed |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the dialog when the outside is clicked |
| `defaultOpen` | `boolean` | No | The initial open state of the dialog when rendered.
Use when you don't need to control the open state of the dialog. |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is closed |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  trigger: string
  positioner: string
  backdrop: string
  content: string
  closeTrigger: string
  title: string
  description: string
}>` | No | The ids of the elements in the dialog. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is opened |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function to call when the dialog's open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `open` | `boolean` | No | The controlled open state of the dialog |
| `persistentElements` | `(() => Element | null)[]` | No | Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the dialog when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the dialog was opened |
| `role` | `'dialog' | 'alertdialog'` | No | The dialog's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the dialog when it's opened |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-nested]` | dialog |
| `[data-has-nested]` | dialog |


**Description Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDialogReturn` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | trigger |
| `[data-state]` | "open" | "closed" |


#### Solid

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `aria-label` | `string` | No | Human readable label for the dialog, in event the dialog title is not rendered |
| `closeOnEscape` | `boolean` | No | Whether to close the dialog when the escape key is pressed |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the dialog when the outside is clicked |
| `defaultOpen` | `boolean` | No | The initial open state of the dialog when rendered.
Use when you don't need to control the open state of the dialog. |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is closed |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  trigger: string
  positioner: string
  backdrop: string
  content: string
  closeTrigger: string
  title: string
  description: string
}>` | No | The ids of the elements in the dialog. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is opened |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function to call when the dialog's open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `open` | `boolean` | No | The controlled open state of the dialog |
| `persistentElements` | `(() => Element | null)[]` | No | Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the dialog when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the dialog was opened |
| `role` | `'dialog' | 'alertdialog'` | No | The dialog's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the dialog when it's opened |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-nested]` | dialog |
| `[data-has-nested]` | dialog |


**Description Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDialogReturn` | Yes |  |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'h2'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'button'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | trigger |
| `[data-state]` | "open" | "closed" |


#### Vue

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `aria-label` | `string` | No | Human readable label for the dialog, in event the dialog title is not rendered |
| `closeOnEscape` | `boolean` | No | Whether to close the dialog when the escape key is pressed |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the dialog when the outside is clicked |
| `defaultOpen` | `boolean` | No | The initial open state of the dialog when rendered.
Use when you don't need to control the open state of the dialog. |
| `finalFocusEl` | `() => HTMLElement | null` | No | Element to receive focus when the dialog is closed |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  trigger: string
  positioner: string
  backdrop: string
  content: string
  closeTrigger: string
  title: string
  description: string
}>` | No | The ids of the elements in the dialog. Useful for composition. |
| `initialFocusEl` | `() => HTMLElement | null` | No | Element to receive focus when the dialog is opened |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it |
| `open` | `boolean` | No | The controlled open state of the dialog |
| `persistentElements` | `(() => Element | null)[]` | No | Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the dialog when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the dialog was opened |
| `role` | `'dialog' | 'alertdialog'` | No | The dialog's role |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the dialog when it's opened |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-nested]` | dialog |
| `[data-has-nested]` | dialog |


**Description Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `DialogApi<PropTypes>` | Yes |  |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Trigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | trigger |
| `[data-state]` | "open" | "closed" |


#### Svelte

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `aria-label` | `string` | No | Human readable label for the dialog, in event the dialog title is not rendered |
| `closeOnEscape` | `boolean` | No | Whether to close the dialog when the escape key is pressed |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the dialog when the outside is clicked |
| `defaultOpen` | `boolean` | No | The initial open state of the dialog when rendered.
Use when you don't need to control the open state of the dialog. |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is closed |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  trigger: string
  positioner: string
  backdrop: string
  content: string
  closeTrigger: string
  title: string
  description: string
}>` | No | The ids of the elements in the dialog. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is opened |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function to call when the dialog's open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `open` | `boolean` | No | The controlled open state of the dialog |
| `persistentElements` | `(() => Element | null)[]` | No | Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the dialog when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the dialog was opened |
| `role` | `'alertdialog' | 'dialog'` | No | The dialog's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the dialog when it's opened |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Backdrop Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Backdrop Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | backdrop |
| `[data-state]` | "open" | "closed" |


**CloseTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Content Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Content Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | dialog |
| `[data-part]` | content |
| `[data-state]` | "open" | "closed" |
| `[data-nested]` | dialog |
| `[data-has-nested]` | dialog |


**Description Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'p'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Positioner Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseDialogReturn` | Yes |  |


**Title Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'h2'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Trigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Trigger Data Attributes:**

| 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 | Description |
|----------|------|-------------|
| `open` | `boolean` | Whether the dialog is open |
| `setOpen` | `(open: boolean) => void` | Function to open or close the dialog |


## Accessibility

Complies with the [Dialog WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).

### Keyboard Support

<KeyBindingsTable id="dialog" />