Skip to content

Dialog ​

A confirmation/alert dialog for user decisions. Use the template component VxDialog, the programmatic useConfirm() composable, or combine with VxModal for multi-step wizards.

Stepped Dialog ​

A multi-step wizard using VxModal with Previous / Next navigation and a final Save button.

Full Example Code
vue
<script setup>
import { ref, computed } from 'vue'

const show = ref(false)
const step = ref(0)
const saving = ref(false)
const form = ref({
  name: '',
  email: '',
  notifications: true,
  theme: 'system',
  agree: false,
})

const steps = [
  { title: 'Account', desc: 'Basic information' },
  { title: 'Preferences', desc: 'Customize experience' },
  { title: 'Confirm', desc: 'Review & save' },
]

const isFirst = computed(() => step.value === 0)
const isLast = computed(() => step.value === steps.length - 1)

function open() {
  step.value = 0
  form.value = { name: '', email: '', notifications: true, theme: 'system', agree: false }
  saving.value = false
  show.value = true
}
function next() { if (!isLast.value) step.value++ }
function prev() { if (!isFirst.value) step.value-- }
function save() {
  saving.value = true
  setTimeout(() => {
    saving.value = false
    show.value = false
    // submit form.value to API...
  }, 1500)
}
</script>

<template>
  <button @click="open">Open Wizard</button>

  <VxModal v-model="show" size="sm" persistent :closable="false">
    <!-- Header with step indicator -->
    <template #header>
      <div style="width: 100%">
        <div style="display: flex; justify-content: space-between; margin-bottom: 16px">
          <h3>Create Account</h3>
          <span>Step {{ step + 1 }} of {{ steps.length }}</span>
        </div>
        <div class="wiz-steps">
          <div
            v-for="(s, i) in steps" :key="i"
            class="wiz-step"
            :class="{ 'wiz-step--active': i === step, 'wiz-step--done': i < step }"
          >
            <div class="wiz-dot">
              <svg v-if="i < step" width="12" height="12" viewBox="0 0 24 24"
                fill="none" stroke="currentColor" stroke-width="2.5"
                stroke-linecap="round" stroke-linejoin="round">
                <path d="M5 12l5 5L20 7" />
              </svg>
              <span v-else>{{ i + 1 }}</span>
            </div>
            <div class="wiz-label">
              <div class="wiz-label-title">{{ s.title }}</div>
              <div class="wiz-label-desc">{{ s.desc }}</div>
            </div>
          </div>
        </div>
      </div>
    </template>

    <!-- Step content with slide transition -->
    <Transition name="wiz-fade" mode="out-in">
      <!-- Step 1: Account -->
      <div v-if="step === 0" key="s0" class="wiz-body">
        <label class="wiz-field">
          <span class="wiz-field-label">Full Name</span>
          <input v-model="form.name" placeholder="John Doe" class="wiz-input" />
        </label>
        <label class="wiz-field">
          <span class="wiz-field-label">Email</span>
          <input v-model="form.email" type="email" placeholder="john@example.com"
            class="wiz-input" />
        </label>
      </div>

      <!-- Step 2: Preferences -->
      <div v-else-if="step === 1" key="s1" class="wiz-body">
        <label class="wiz-field wiz-field--row">
          <span class="wiz-field-label">Email notifications</span>
          <VxSwitch v-model="form.notifications" />
        </label>
        <div class="wiz-field">
          <span class="wiz-field-label">Theme</span>
          <div class="wiz-radios">
            <label class="wiz-radio"
              :class="{ 'wiz-radio--active': form.theme === 'light' }">
              <input v-model="form.theme" type="radio" value="light" /> Light
            </label>
            <label class="wiz-radio"
              :class="{ 'wiz-radio--active': form.theme === 'dark' }">
              <input v-model="form.theme" type="radio" value="dark" /> Dark
            </label>
            <label class="wiz-radio"
              :class="{ 'wiz-radio--active': form.theme === 'system' }">
              <input v-model="form.theme" type="radio" value="system" /> System
            </label>
          </div>
        </div>
      </div>

      <!-- Step 3: Review & Confirm -->
      <div v-else key="s2" class="wiz-body">
        <div class="wiz-review">
          <div class="wiz-review-row">
            <span class="wiz-review-label">Name</span>
            <span class="wiz-review-value">{{ form.name || '—' }}</span>
          </div>
          <div class="wiz-review-row">
            <span class="wiz-review-label">Email</span>
            <span class="wiz-review-value">{{ form.email || '—' }}</span>
          </div>
          <div class="wiz-review-row">
            <span class="wiz-review-label">Notifications</span>
            <span class="wiz-review-value">{{ form.notifications ? 'On' : 'Off' }}</span>
          </div>
          <div class="wiz-review-row">
            <span class="wiz-review-label">Theme</span>
            <span class="wiz-review-value">{{ form.theme }}</span>
          </div>
        </div>
        <label class="wiz-field wiz-field--row" style="margin-top: 12px">
          <input v-model="form.agree" type="checkbox" />
          <span>I agree to the Terms and Privacy Policy</span>
        </label>
      </div>
    </Transition>

    <!-- Footer: Previous / Next / Save -->
    <template #footer>
      <button v-if="isFirst" class="wiz-btn wiz-btn--ghost" @click="show = false">
        Cancel
      </button>
      <button v-else class="wiz-btn wiz-btn--ghost" @click="prev">
        ← Previous
      </button>

      <button v-if="!isLast" class="wiz-btn wiz-btn--primary" @click="next">
        Next →
      </button>
      <button v-else class="wiz-btn wiz-btn--save"
        :disabled="!form.agree || saving" @click="save">
        {{ saving ? 'Saving...' : 'Save Account' }}
      </button>
    </template>
  </VxModal>
</template>

Programmatic API (useConfirm) ​

Call from <script setup> — no template needed. Returns a Promise that resolves on confirm and rejects on cancel.

vue
<script setup>
import { useConfirm } from 'veloxui'

const confirm = useConfirm()

// Basic — string shorthand
function deleteItem() {
  confirm('Are you sure?')
    .then(() => {
      // confirmed
      console.log('Deleted!')
    })
    .catch(() => {
      // cancelled
      console.log('Cancelled')
    })
}

// Typed shorthand — .danger(), .success(), .warning(), .info()
function deleteAccount() {
  confirm.danger({
    title: 'Delete Account',
    description: 'This cannot be undone.',
    confirmText: 'Delete',
  }).then(() => {
    api.deleteAccount()
  })
}

// Async / await
async function removeMember() {
  try {
    await confirm.danger({
      title: 'Remove Member',
      description: 'They will lose access.',
      confirmText: 'Remove',
    })
    await api.removeMember(id)
  } catch {
    // user cancelled
  }
}

// Info alert (no cancel button)
function showAlert() {
  confirm.info({
    title: 'Update Available',
    confirmText: 'OK',
    showCancel: false,
  }).then(() => { ... })
}
</script>

Template Component ​

For cases where you need slots or reactive loading state.

Types ​

vue
<template>
  <VxDialog type="info" title="Info" description="Saved." />
  <VxDialog type="success" title="Success" confirm-text="Great!" :show-cancel="false" />
  <VxDialog type="warning" title="Unsaved Changes" confirm-text="Leave" cancel-text="Stay" />
  <VxDialog type="danger" title="Delete" confirm-text="Delete" />
</template>

Without Icon ​

vue
<template>
  <VxDialog :icon="false" title="Confirm" />
</template>

Confirm Only ​

vue
<template>
  <VxDialog type="success" confirm-text="OK" :show-cancel="false" />
</template>

Closable ​

vue
<template>
  <VxDialog closable title="Notice" />
</template>

Loading ​

vue
<template>
  <VxDialog type="danger" :loading="isDeleting" @confirm="handleDelete" />
</template>

Custom Button Text ​

vue
<template>
  <VxDialog confirm-text="Discard" cancel-text="Keep" />
</template>

Custom Content ​

vue
<template>
  <VxDialog v-model="show" title="Rate Limit">
    <p>Custom HTML in the default slot.</p>
  </VxDialog>
</template>

Persistent ​

vue
<template>
  <VxDialog :persistent="false" close-on-overlay />
</template>

Sizes ​

vue
<template>
  <VxDialog size="sm" />
  <VxDialog size="md" />
  <VxDialog size="lg" />
</template>

Position ​

vue
<template>
  <VxDialog position="top-left" />
  <VxDialog position="top-center" />
  <VxDialog position="top-right" />
  <VxDialog position="center" />  <!-- default -->
  <VxDialog position="bottom-left" />
  <VxDialog position="bottom-center" />
  <VxDialog position="bottom-right" />
</template>

Props ​

PropTypeDefaultDescription
modelValuebooleanfalseShow/hide (v-model)
titlestring''Dialog title
descriptionstring''Description text
type'info' | 'success' | 'warning' | 'danger''info'Icon and button color
size'sm' | 'md' | 'lg''sm'Dialog width
iconbooleantrueShow type icon
closablebooleanfalseShow close button
closeOnOverlaybooleanfalseClose on overlay click
closeOnEscbooleantrueClose on Escape
confirmTextstring'Confirm'Confirm button text
cancelTextstring'Cancel'Cancel button text
showCancelbooleantrueShow cancel button
loadingbooleanfalseLoading state
persistentbooleantrueShake on overlay click
position'center' | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right''center'Dialog position

Events ​

EventDescription
update:modelValueValue changed
confirmConfirm clicked
cancelCancel clicked
closeDialog closed
openedEnter transition finished
closedLeave transition finished

Slots ​

SlotDescription
defaultCustom body content (replaces description)

useConfirm API ​

ts
import { useConfirm } from 'veloxui'

const confirm = useConfirm()

// Returns Promise<void>
// Resolves on confirm, rejects on cancel
confirm(options: ConfirmOptions | string): Promise<void>

// Type shortcuts
confirm.info(options)
confirm.success(options)
confirm.warning(options)
confirm.danger(options)

ConfirmOptions ​

OptionTypeDefaultDescription
titlestring''Dialog title
descriptionstring''Description text
type'info' | 'success' | 'warning' | 'danger''info'Dialog type
size'sm' | 'md' | 'lg''sm'Dialog width
iconbooleantrueShow icon
closablebooleanfalseShow X button
confirmTextstring'Confirm'Confirm button
cancelTextstring'Cancel'Cancel button
showCancelbooleantrueShow cancel
persistentbooleantrueShake on backdrop