Dialog
A headless modal dialog component using the native HTML dialog element.
Uses the native dialog element with showModal(). Safari 15.4+ is required; older versions have no support.
Usage
The Dialog component leverages the native showModal() API for proper modal behavior including focus trapping, backdrop rendering, and escape key handling. It provides v-model support for open/closed state management.
Anatomy
<script setup lang="ts">
import { Dialog } from '@vuetify/v0'
</script>
<template>
<Dialog.Root>
<Dialog.Activator />
<Dialog.Content>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
</Dialog.Content>
</Dialog.Root>
</template>Recipes
Click-Outside Dismissal
By default, clicking the backdrop closes the dialog. Set closeOnClickOutside to false on Dialog.Content to prevent this:
<template>
<Dialog.Content :close-on-click-outside="false">
<!-- Dialog won't close on backdrop click -->
</Dialog.Content>
</template>Blocking Dialogs
The blocking prop disables scrim-based dismissal entirely — the dialog can only be closed programmatically. Use this for critical confirmations where the user must make an explicit choice:
<template>
<Dialog.Content blocking>
<!-- No scrim, no click-outside close — must use Dialog.Close or v-model -->
</Dialog.Content>
</template>Accessibility
Dialog renders its panel through the native <dialog> element opened with showModal(), so focus trapping, backdrop rendering, inerting of the page behind it, and Escape-to-close all come from the browser.
ARIA Attributes
| Attribute | Value | Element |
|---|---|---|
aria-haspopup | dialog | Activator |
aria-expanded | true / false | Activator |
role | dialog | Content |
aria-modal | true | Content |
aria-labelledby | Title element ID | Content |
aria-describedby | Description element ID | Content |
aria-label | Localized “Close” string | Close |
Dialog.Title and Dialog.Description generate the IDs referenced by aria-labelledby and aria-describedby. Render them inside Dialog.Content so the dialog has an accessible name and description.
Keyboard Navigation
| Key | Action |
|---|---|
Tab / Shift + Tab | Cycles focus through focusable controls inside Content (trapped by showModal()) |
Escape | Closes the dialog (native cancel event) |
Enter / Space | Activates the focused control |
Focus management
showModal() moves focus into the dialog on open, and the browser traps Tab within it while it stays open. Dialog.Activator renders a <button> by default — carrying aria-haspopup="dialog" and aria-expanded — and opens the dialog on click.
FAQ
The native <dialog> element with showModal() promotes itself to the browser’s top layer — a rendering surface that sits above all normal document content regardless of z-index, and makes everything outside its subtree inert. Any overlay rendered outside the dialog (e.g. a Snackbar.Portal teleported to body, or a Tooltip inside a portal) appears below the dialog, not above it. This is a browser-level constraint, not a v0 bug — but Snackbar.Portal handles it for you by default (see below).
Snackbar.Portal handles this automatically: it defaults to teleport="top-layer", which teleports the snackbar into the topmost open modal so it shares the dialog’s top-layer context and stays interactive. When no modal is open it falls back to body.
<template>
<!-- teleport="top-layer" is the default — no explicit prop needed -->
<Snackbar.Portal>
<Snackbar.Queue v-slot="{ items }">
<!-- ... -->
</Snackbar.Queue>
</Snackbar.Portal>
</template>To opt out, set teleport="body" (always body) or :teleport="false" (render inline). See Snackbar for the full teleport option reference.
:close-on-click-outside="false" on Dialog.Content stops backdrop clicks from closing the dialog. blocking goes further and disables scrim-based dismissal entirely, so the dialog can only be closed programmatically via Dialog.Close or v-model — reach for it on critical confirmations that require an explicit choice.