Dialog

A flexible overlay for showing messages, forms, or actions. Keeps focus on content while allowing clear, user-friendly interactions.

Destructive confirm

Create / edit form

State-driven actions

Action layout

A single action on a small dialog (xs, sm, md) renders full-width. Everything else sits side-by-side at natural width, right-aligned. Using the #actions slot opts out — you own the layout.

Single-CTA form

Auto-focus a field

Full-canvas (bare)

Master / detail browser

Imperative API

The dialog.* helpers cover the confirm-family surface — confirm and the danger preset — without mounting a <Dialog> yourself.

Pass an actions array to dialog.confirm (or dialog.danger) when a flow needs more than the default confirm + cancel pair. Each action accepts full Button props and its own awaited onClick; the clicked button shows a loading spinner while its handler is pending and every other button is disabled until it settles. Throwing from onClick surfaces inline via the shared error region.

dialog.danger is a one-line preset for irreversible actions. It forces theme: 'red', defaults the icon to a warning triangle, and defaults confirmLabel to 'Delete'. Everything confirm accepts (including actions[]) is forwarded through.

Prompt

dialog.prompt collects structured input through a fields[] array. Each field renders through FormControl, so any FormControl type works — including text, select, checkbox, and combobox. For combobox fields, allowCreate passes the typed query through as the value when nothing in the list matches — handy for category-style fields where users can add new entries inline.

Each field can also declare a validate function. It runs after the built-in required check, in parallel across all fields, and returns a non-empty string to mark the field invalid (shown inline below it) or null for valid. The second argument is a snapshot of every field's current value, so validators can reference siblings. The submit button keeps its loading state while async validators settle.

API Reference

Show types
typescript
import type { ButtonProps } from '../Button'

export type DialogSize =
  | 'xs'
  | 'sm'
  | 'md'
  | 'lg'
  | 'xl'
  | '2xl'
  | '3xl'
  | '4xl'
  | '5xl'
  | '6xl'
  | '7xl'

export type DialogTheme = 'yellow' | 'blue' | 'red' | 'green'

export type DialogPosition = 'center' | 'top'

/** Legacy icon appearance — replaced by `theme`. */
export type DialogIconAppearance = 'warning' | 'info' | 'danger' | 'success'

export type DialogIcon = {
  name: string
  /** Color tone. Replaces deprecated `appearance`. */
  theme?: DialogTheme
  /** @deprecated Use `theme` instead. */
  appearance?: DialogIconAppearance
}

export type DialogActionContext = {
  close: () => void
}

export type DialogAction = ButtonProps & {
  onClick?: (context: DialogActionContext) => void | Promise<void>
}

/** A `DialogAction` augmented with a reactive `loading` flag, as surfaced to the `#actions` slot. */
export type DialogReactiveAction = DialogAction & { loading: boolean }

/** Legacy blob — accepted for back-compat, warns once. */
export type DialogOptions = {
  title?: string
  message?: string
  size?: DialogSize
  icon?: string | DialogIcon
  actions?: DialogAction[]
  position?: DialogPosition
  paddingTop?: string | number
}

export interface DialogProps {
  // Visibility — both supported, `open` is canonical.
  /** Controls whether the dialog is open (v-model:open). Canonical. */
  open?: boolean

  /** Controls whether the dialog is open (v-model). Also supported. */
  modelValue?: boolean

  // Content.
  /** Dialog title. Renders the auto-header. */
  title?: string

  /** Description text rendered below the title. */
  message?: string

  /** Icon shown next to the title in the auto-header. */
  icon?: string | DialogIcon

  // Layout.
  /** Max-width size of the dialog. Default `'lg'`. */
  size?: DialogSize

  /** Vertical placement. Default `'center'`. */
  position?: DialogPosition

  /** Overrides the position-based top padding (escape hatch). */
  paddingTop?: string | number

  // Actions.
  /** Footer action buttons. */
  actions?: DialogAction[]

  // Behavior.
  /** Allow outside-click and Escape to close. Default `true`. */
  dismissable?: boolean

  /** Show the top-right close button. Default `true`. */
  showCloseButton?: boolean

  /** Drop the chrome: no padded card, no auto-header, no auto-actions. Default `false`. */
  bare?: boolean

  // Deprecated.
  /** @deprecated Use `dismissable` (inverted) instead. */
  disableOutsideClickToClose?: boolean

  /** @deprecated Use flat top-level props instead. */
  options?: DialogOptions
}

export interface DialogEmits {
  /** Fired when the dialog open state changes via `v-model:open`. */
  'update:open': [value: boolean]

  /** Fired when the dialog open state changes via `v-model`. */
  'update:modelValue': [value: boolean]

  /** Fired when the dialog transitions to closed. */
  close: []

  /** Fired after the close animation finishes. */
  'after-leave': []
}

/** Scoped payload exposed to every Dialog slot. */
export interface DialogSlotProps {
  /** Closes the dialog. */
  close: () => void
}

/** Scoped payload for the `#actions` slot. */
export interface DialogActionsSlotProps extends DialogSlotProps {
  /** Reactive list of resolved actions (with `loading` state) for re-laying-out auto-rendered buttons. */
  actions: DialogReactiveAction[]
}

export interface DialogExposed {
  /** Closes the dialog. */
  close: () => void
}

export interface DialogSlots {
  /** Main content rendered inside the padded card. Exposes `{ close }`. */
  default?: (props: DialogSlotProps) => any

  /** Title area; accepts arbitrary content (extra buttons next to title, etc.). Exposes `{ close }`. */
  title?: (props: DialogSlotProps) => any

  /** Footer override; exposes `{ close, actions }`. */
  actions?: (props: DialogActionsSlotProps) => any

  /** @deprecated Use `#default` + `bare` prop. */
  body?: () => any

  /** @deprecated Use `#default`. */
  'body-main'?: () => any

  /** @deprecated Use `#title` for extras (no direct replacement). */
  'body-header'?: () => any

  /** @deprecated Use `#title`. */
  'body-title'?: () => any

  /** @deprecated Use `#default`. */
  'body-content'?: () => any
}
Prop Default Type
open
undefined
boolean

Controls whether the dialog is open (v-model:open). Canonical.

modelValue
undefined
boolean

Controls whether the dialog is open (v-model). Also supported.

title
string

Dialog title. Renders the auto-header.

message
string

Description text rendered below the title.

icon
string | DialogIcon

Icon shown next to the title in the auto-header.

size
undefined
DialogSize

Max-width size of the dialog. Default `'lg'`.

position
undefined
DialogPosition

Vertical placement. Default `'center'`.

paddingTop
string | number

Overrides the position-based top padding (escape hatch).

actions
DialogAction[]

Footer action buttons.

dismissable
true
boolean

Allow outside-click and Escape to close. Default `true`.

showCloseButton
true
boolean

Show the top-right close button. Default `true`.

bare
false
boolean

Drop the chrome: no padded card, no auto-header, no auto-actions. Default `false`.

disableOutsideClickToClose
undefined

Deprecated — Use `dismissable` (inverted) instead.

options

Deprecated — Use flat top-level props instead.

Slot Payload
default
DialogSlotProps

Main content rendered inside the padded card. Exposes `{ close }`.

title
DialogSlotProps

Title area; accepts arbitrary content (extra buttons next to title, etc.). Exposes `{ close }`.

actions
DialogActionsSlotProps

Footer override; exposes `{ close, actions }`.

body

Deprecated — Use `#default` + `bare` prop.

body-main

Deprecated — Use `#default`.

body-header

Deprecated — Use `#title` for extras (no direct replacement).

body-title

Deprecated — Use `#title`.

body-content

Deprecated — Use `#default`.

Event Payload
update:modelValue
[value: boolean]

Fired when the model value changes.

update:open
[value: boolean]

Fired when the open state changes.

close
[]

Fired when the component closes.

after-leave
[]