Skip to main content
The BModal component is a molecule that provides a complete modal dialog with optional form integration, customizable primary/secondary buttons, and flexible content slots.

Props

state
Record<string, any> | null
default:"null"
The state object for form management. When provided, enables form mode.
schema
Record<string, any> | null
default:"null"
The schema for form validation (e.g., Zod, Yup)
title
string
default:""
The title displayed at the top of the modal
description
string
default:""
The description text shown below the title
text
string
default:""
Simple text content when you don’t need the slot with complex markup
primaryButtonText
string
default:"Guardar"
Text label for the primary action button
primaryButtonColor
SemanticColors
default:"primary"
Color variant for the primary button
secondaryButtonText
string
default:"Cancelar"
Text label for the secondary action button
Whether to display the modal footer with action buttons
isPrimaryButtonDisabled
boolean
default:"false"
Whether the primary button should be disabled
hasButtonsBlock
boolean
default:"false"
Whether buttons should be displayed as full-width blocks
classButtons
ClassNameValue
default:""
Additional CSS classes for the button container
class
ClassNameValue
default:""
Additional CSS classes for the modal dialog

Model

v-model
boolean
required
Controls the open/closed state of the modal
v-model:formRef
Ref<any>
Reference to the form element (when using form mode)

Events

on-click-primary-button
() => void
Emitted when the primary button is clicked
on-click-secondary-button
() => void
Emitted when the secondary button is clicked. Automatically closes the modal.
on-submit
() => void
Emitted when the form is submitted (only in form mode)

Slots

header
Custom header content (replaces title and description)
default
Main body content (used when text prop is not provided)
Custom footer content (replaces default action buttons)

Usage

Basic Modal

<template>
  <div>
    <UButton @click="isOpen = true">Open Modal</UButton>
    
    <BModal 
      v-model="isOpen"
      title="Confirm Action"
      description="Are you sure you want to continue?"
      text="This action cannot be undone."
      @on-click-primary-button="handleConfirm"
    />
  </div>
</template>

<script setup>
const isOpen = ref(false)

function handleConfirm() {
  console.log('Confirmed')
  isOpen.value = false
}
</script>
<template>
  <BModal
    v-model="isOpen"
    v-model:formRef="formRef"
    title="Edit Profile"
    :state="formState"
    :schema="formSchema"
    primary-button-text="Save Changes"
    @on-submit="handleSubmit"
  >
    <UFormField label="Name" name="name">
      <UInput v-model="formState.name" />
    </UFormField>
    
    <UFormField label="Email" name="email">
      <UInput v-model="formState.email" type="email" />
    </UFormField>
  </BModal>
</template>

<script setup>
import { z } from 'zod'

const isOpen = ref(false)
const formRef = ref(null)

const formState = reactive({
  name: '',
  email: ''
})

const formSchema = z.object({
  name: z.string().min(2),
  email: z.string().email()
})

function handleSubmit() {
  console.log('Form submitted', formState)
  isOpen.value = false
}
</script>
<template>
  <BModal
    v-model="isOpen"
    title="Custom Actions"
  >
    <p>Modal content here</p>
    
    <template #footer>
      <div class="flex justify-between w-full">
        <UButton variant="ghost" @click="handleOption1">Option 1</UButton>
        <UButton variant="ghost" @click="handleOption2">Option 2</UButton>
        <UButton color="primary" @click="handleSave">Save</UButton>
      </div>
    </template>
  </BModal>
</template>
<template>
  <BModal
    v-model="isOpen"
    title="Information"
    :has-footer="false"
  >
    <p>This modal has no footer buttons.</p>
  </BModal>
</template>

Full-Width Buttons

<template>
  <BModal
    v-model="isOpen"
    title="Confirm Delete"
    text="This will permanently delete the item."
    primary-button-text="Delete"
    primary-button-color="error"
    has-buttons-block
    @on-click-primary-button="handleDelete"
  />
</template>

Disabled Primary Button

<template>
  <BModal
    v-model="isOpen"
    title="Submit Form"
    :is-primary-button-disabled="!formValid"
    @on-click-primary-button="handleSubmit"
  >
    <p>Fill out the form to enable submission</p>
  </BModal>
</template>

<script setup>
const formValid = ref(false)
</script>

Form Mode

When state prop is provided, the modal body is wrapped in a UForm component:
const formProps = computed(() => {
  return props.state ? { ref: 'formRef', state: props.state, schema: props.schema } : {}
})
This enables:
  • Form validation
  • Submit event handling
  • Form ref access via v-model:formRef
The default footer uses the DActionButtons component with:
  • Secondary button on the left (closes modal automatically)
  • Primary button on the right
  • Configurable colors, text, and disabled states
Source: /home/daytona/workspace/source/app/components/b/modal/b-modal.vue:152

Build docs developers (and LLMs) love