If you've ever built multi-step forms in Vue, you know the usual pain:
- Managing step state manually
- Handling validation across steps
- Conditionally showing steps
- Keeping everything in sync
What if you could define your entire wizard in a single object?
With vue3-form-wizard v1, you can do exactly that using the new Schema Mode.
β¨ What is Schema Mode?
Schema Mode is a declarative API that lets you build a multi-step wizard without writing <tab-content> components.
Instead, you define everything like this:
- Steps
- Conditions
- Validation
- Data structure
π All inside one schema object.
π§ Why Use Schema Mode?
- π§© Centralized configuration
- π Reactive step conditions
- β Built-in validation per step
- π Shared state across all steps
- β‘ Faster development (less boilerplate)
π¦ Installation
npm install vue3-form-wizard
π Basic Example
Letβs build a simple 2-step wizard:
<template>
<form-wizard
:schema="schema"
:schema-components="schemaComponents"
v-model="wizardData"
@on-complete="handleComplete"
/>
</template>
<script setup>
import { ref } from 'vue'
import { FormWizard } from 'vue3-form-wizard'
import 'vue3-form-wizard/dist/style.css'
import IntroStep from './IntroStep.vue'
import ReviewStep from './ReviewStep.vue'
const schema = {
initialData: { plan: 'basic' },
steps: [
{ id: 'intro', title: 'Intro', component: 'IntroStep' },
{ id: 'review', title: 'Review', component: 'ReviewStep' },
],
}
const schemaComponents = { IntroStep, ReviewStep }
const wizardData = ref({ plan: 'basic' })
const handleComplete = () => {
alert('Done!')
}
</script>
π§© How It Works
1. initialData β Your Single Source of Truth
initialData: {
plan: 'basic',
email: ''
}
All steps read and update this shared object.
2. Steps Configuration
steps: [
{ id: 'plan', title: 'Plan', component: 'PlanStep' },
{ id: 'contact', title: 'Contact', component: 'ContactStep' },
]
Each step is just metadata β no template nesting required.
π Two-Way Data Binding in Steps
Each step receives:
-
dataβ current wizard state -
updateData()β function to update state
Example:
<script setup>
defineProps(['data', 'updateData'])
const onChange = (e) => {
updateData({ plan: e.target.value })
}
</script>
π― Conditional Steps (Dynamic Flow)
Want to show steps based on user input?
{
id: 'premium',
title: 'Premium',
component: 'PremiumStep',
condition: ({ data }) => data.plan === 'premium'
}
π This step only appears if the user selects premium plan.
β Validation (Sync + Async)
You can block navigation easily:
{
id: 'email',
title: 'Email',
component: 'EmailStep',
validate: ({ data }) => {
const valid = /^[^@]+@[^@]+\.\w+$/.test(data.email || '')
return valid ? true : 'Enter a valid email'
}
}
-
trueβ allow navigation -
stringβ show error message
β‘ Advanced: Define Steps Without .vue Files
You can even define steps inline using defineComponent:
const NameStep = defineComponent({
props: ['data', 'updateData'],
setup(props) {
return () => h('input', {
value: props.data.name,
onInput: e => props.updateData({ name: e.target.value })
})
}
})
π Perfect for dynamic or generated UIs.
π Full Example (Minimal)
const schema = {
initialData: { name: '' },
steps: [
{ id: 'name', title: 'Name', component: 'NameStep' },
{ id: 'summary', title: 'Summary', component: 'SummaryStep' },
]
}
π Schema Mode vs Classic Mode
| Feature | Schema Mode | Classic Mode |
|---|---|---|
| Config-driven | β | β |
| Dynamic steps | β | β οΈ manual |
| Clean structure | β | β |
| Flexibility | β | β |
π Final Thoughts
Schema Mode turns multi-step forms into a data problem instead of a UI problem.
Instead of juggling components and state, you:
π Define everything once
π Let the wizard handle the rest
π Resources
- π Docs: https://vue3-form-wizard-document.netlify.app/
- π Demo: https://vue3-form-wizard-document.netlify.app/demos/#schema-mode-v1
- π§ͺ Playground: https://vue3-form-wizard-document.netlify.app/playground/
π¬ What Do You Think?
Would you use schema-driven forms in your projects?
Or do you prefer full control with traditional components?
Let me know in the comments π
Top comments (0)