Playground
Simple
Â
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete">
<tab-content title="Personal details">
My first tab content
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import {FormWizard,TabContent} from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
export default {
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
 Â
Small Tab size 🆕
Â
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete" step-size="xs">
<tab-content title="Personal details">
You Can select 4 size for tabs : xs | sm | md | lg
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import {FormWizard,TabContent} from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
export default {
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
 Â
Square steps
Â
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete" shape="square" color="#3498db">
<tab-content title="Personal details">
My first tab content
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
With font awsome icon 🆕
Â
Â
View Source 💻
<template>
<FormWizard @on-complete="onComplete" color="#094899">
<TabContent title="Personal details" icon="fa fa-user">
My first tab content
</TabContent>
<TabContent title="Additional Info" icon="fa fa-gear">
My second tab content
</TabContent>
<TabContent title="Last step" icon="fa fa-city">
Yuhuuu! This seems pretty damn simple
</TabContent>
</FormWizard>
</template>
<script>
//local registration
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
//component code
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
<style>
@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css");
</style>
With Themify icon 🆕
Â
Â
View Source 💻
<template>
<FormWizard @on-complete="onComplete" color="#c11c67">
<TabContent title="Personal details" icon="ti-user">
My first tab content
</TabContent>
<TabContent title="Additional Info" icon="ti-info-alt">
My second tab content
</TabContent>
<TabContent title="Last step" icon="ti-credit-card">
Yuhuuu! This seems pretty damn simple
</TabContent>
</FormWizard>
</template>
<script>
//local registration
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
//component code
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
<style>
@import url("https://cdn.jsdelivr.net/gh/lykmapipo/themify-icons@0.1.2/css/themify-icons.css");
</style>
Custom icon 🆕
Â
Â
View Source 💻
<template>
<FormWizard @on-complete="onComplete" color="#dd9c1d">
<TabContent
title="Personal details"
customIcon="<img src='./vue.png' width='40px' />"
>
<div >
You can add your HTML tag (svg,img,i,...)
<br>
also can add your custom component (b-img,b-icon,....)
</div>
</TabContent>
<TabContent
title="Additional Info"
:customIcon="`<img src='${viteIcon}' width='35px' />`" >
add your dynamic source
</TabContent>
<TabContent title="Last step" :customIcon="nuxtIcon">
create tag in computed
</TabContent>
</FormWizard>
</template>
<script>
//local registration
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
data() {
return {
viteIcon:'https://camo.githubusercontent.com/61e102d7c605ff91efedb9d7e47c1c4a07cef59d3e1da202fd74f4772122ca4e/68747470733a2f2f766974656a732e6465762f6c6f676f2e737667'
}
},
//component code
components: {
FormWizard,
TabContent,
},
computed:{
nuxtIcon(){
return `<img src='https://nuxtjs.org/design-kit/colored-logo.svg' width='35px' />`
}
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
compositon API 🆕
Â
Â
View Source 💻
<script setup>
//local registration
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
function onComplete() {
alert("Yay. Done!");
}
</script>
<template>
<FormWizard @on-complete="onComplete" color="#094899">
<TabContent title="Personal details" icon="fa fa-user">
My first tab content
</TabContent>
<TabContent title="Additional Info" icon="fa fa-gear">
My second tab content
</TabContent>
<TabContent title="Last step" icon="fa fa-city">
Yuhuuu! This seems pretty damn simple
</TabContent>
</FormWizard>
</template>
<style>
@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css");
</style>
Step index
Â
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete"
:start-index="1"
color="#e67e22">
<tab-content title="Personal details"
>
My first tab content
</tab-content>
<tab-content title="Additional Info"
>
My second tab content
</tab-content>
<tab-content title="Last step"
>
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import {FormWizard,TabContent} from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
export default {
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
Custom button and title text
Â
Â
View Source 💻
<template>
<form-wizard
@on-complete="onComplete"
title="This is a new title"
subtitle="And a new subtitle"
shape="tab"
back-button-text="Go back!"
next-button-text="Go next!"
finish-button-text="We're there"
color="#9b59b6"
>
<tab-content title="Personal details">
My first tab content
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
name: "CustomButtonAndTitleText",
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
Custom title slot
Â
This will replace my whole title
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete" shape="tab" color="#e67e22">
<template #title>This will replace my whole title</template>
<tab-content title="Personal details">
My first tab content
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
name: "CustomTitleSlost",
components: {
FormWizard,
TabContent,
},
methods: {
onComplete() {
alert("Yay. Done!");
},
},
};
</script>
Call a function before tab switch
Â
Â
View Source 💻
<template>
<form-wizard @on-complete="onComplete" shape="tab" color="#9b59b6">
<tab-content
title="Personal details"
:before-change="beforeTabSwitch"
>
My first tab content
</tab-content>
<tab-content title="Additional Info" >
My second tab content
</tab-content>
<tab-content title="Last step" >
Yuhuuu! This seems pretty damn simple
</tab-content>
</form-wizard>
</template>
<script>
import { FormWizard, TabContent } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
name: "CallFunctionBeforeTabSwitch",
methods: {
onComplete() {
alert("Yay. Done!");
},
beforeTabSwitch() {
alert("This is called before switchind tabs");
return true;
},
},
};
</script>
Custom Slot Props (Footer,Step) 🆕
Â
Custom Slot Title
Â
View Source 💻
<template>
<FormWizard color="#9b59b6">
<!-- Access WizardStep Component directly with all props -->
<template v-slot:step="props">
<WizardStep
:tab="props.tab"
:transition="props.transition"
:index="props.index"
@click="props.navigateToTab(props.index)"
>
<small style="color: blue; font-size: 12px; margin: auto 2px">
Tab{{ props.index + 1 }}
</small>
</WizardStep>
</template>
<!-- title slot -->
<template #title>
<div><h3>Custom Slot Title</h3></div>
</template>
<TabContent title="First">
<section class="form-wizard-sample-card">My first tab content</section>
</TabContent>
<TabContent title="Additional Info">
<section class="form-wizard-sample-card">My second tab content</section>
</TabContent>
<TabContent title="Last step">
<section class="form-wizard-sample-card">
Yuhuuu! This seems pretty damn simple
</section>
</TabContent>
<!-- Access footer directly with all props options -->
<!-- You can create custom design and event -->
<template v-slot:footer="props">
<div class="wizard-footer-left">
<button
v-if="props.activeTabIndex > 0 && !props.isLastStep"
:style="props.fillButtonStyle"
@click.native="props.prevTab()"
class="wizard-button"
>
Previous test
</button>
</div>
<div class="wizard-footer-right">
<button
v-if="!props.isLastStep"
@click.native="props.nextTab()"
class="wizard-button"
:style="props.fillButtonStyle"
>
Next
</button>
<button
v-else
@click.native="confirmMethod"
class="finish-button"
:style="props.fillButtonStyle"
>
{{ props.isLastStep ? "Done" : "Next" }}
</button>
</div>
</template>
</FormWizard>
</template>
<script>
import { FormWizard, TabContent, WizardStep } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
export default {
name: "CustomSlotProps",
components: {
FormWizard,
TabContent,
WizardStep,
},
methods: {
confirmMethod() {
alert("Done");
},
},
};
</script>
<style>
.wizard-card-footer .wizard-footer-left .wizard-button {
padding: 10px;
border-radius: 6px;
cursor: pointer;
}
.wizard-card-footer .wizard-footer-right .wizard-button {
padding: 10px;
border-radius: 6px;
cursor: pointer;
}
.wizard-card-footer .wizard-footer-right .finish-button {
padding: 15px 25px;
border-radius: 6px;
cursor: pointer;
background-color: green !important;
border: 0;
}
.form-wizard-sample-card {
max-width: 300px;
text-align: center;
padding: 30px 10px;
box-shadow: 0px 4px 8px #eee;
margin: 10px auto;
border-radius: 10px;
}
</style>
Schema mode (V1) 🆕
Schema mode lets you define steps declaratively with schema, schema-components, and v-model. See the Schema documentation for details.
Schema: Basic
Â
Â
View Source 💻
<template>
<FormWizard
title="Schema: Basic"
:schema="schema"
:schema-components="schemaComponents"
v-model="schemaData"
color="#9b59b6"
@on-complete="onComplete"
/>
</template>
<script setup>
import { ref } from "vue";
import { FormWizard } from "vue3-form-wizard";
import "vue3-form-wizard/dist/style.css";
const schema = {
initialData: { plan: "basic" },
steps: [
{ id: "intro", title: "Intro", component: "IntroStep" },
{ id: "review", title: "Review", component: "ReviewStep" },
],
};
const schemaComponents = { IntroStep, ReviewStep };
const schemaData = ref({ plan: "basic" });
const onComplete = () => alert("Done!");
</script>
Schema: Conditional Steps
Shows steps based on condition. The Premium step appears only when plan is "premium".
Â
Â
View Source 💻
<template>
<FormWizard
title="Schema: Conditional Steps"
:schema="schema"
:schema-components="schemaComponents"
v-model="schemaData"
@on-complete="onComplete"
/>
</template>
<script setup>
import { FormWizard } from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
import IntroStep from "./schema-steps/IntroStep.vue";
import PremiumStep from "./schema-steps/PremiumStep.vue";
import ReviewStep from "./schema-steps/ReviewStep.vue";
const schema = {
initialData: { plan: "basic" },
steps: [
{ id: "intro", title: "Intro", component: "IntroStep" },
{
id: "premium",
title: "Premium",
component: "PremiumStep",
condition: ({ data }) => data.plan === "premium",
},
{
id: "review",
title: "Review",
component: "ReviewStep",
validate: ({ data }) => (data.plan ? true : "Select a plan"),
},
],
};
const schemaComponents = { IntroStep, PremiumStep, ReviewStep };
const schemaData = ref({ plan: "basic" });
const onComplete = () => alert("Done!");
</script>
Schema: Async Validation
Use validate to block navigation and show custom error messages.
Â
Â
View Source 💻
<template>
<FormWizard
title="Schema: Async Validation"
:schema="schema"
:schema-components="schemaComponents"
v-model="schemaData"
@on-complete="onComplete"
/>
</template>
<script setup>
import { ref } from "vue";
import { FormWizard } from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
import EmailStep from "./schema-steps/EmailStep.vue";
import DoneStep from "./schema-steps/DoneStep.vue";
const schema = {
initialData: { email: "" },
steps: [
{
id: "email",
title: "Email",
component: "EmailStep",
validate: ({ data }) => {
const ok = /^[^@]+@[^@]+\.\w+$/.test(data.email || "");
return ok ? true : "Enter a valid email";
},
},
{ id: "done", title: "Done", component: "DoneStep" },
],
};
const schemaComponents = { EmailStep, DoneStep };
const schemaData = ref({ email: "" });
const onComplete = () => alert("Done!");
</script>
Schema: Render Function
Step components defined as plain render functions (functional components using h()).
Â
Â
View Source 💻
<template>
<FormWizard
title="Schema: Render Function"
:schema="schema"
:schema-components="schemaComponents"
v-model="schemaData"
@on-complete="onComplete"
/>
</template>
<script setup>
import { ref, h } from "vue";
import { FormWizard } from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
const StepA = (props) =>
h("div", [
h("h2", "Step A"),
h("input", {
value: props.data.value,
onInput: (e) => props.updateData({ value: e.target.value }),
}),
]);
const StepB = (props) => h("div", [h("h2", "Step B"), h("p", props.data.value)]);
const schema = {
initialData: { value: "" },
steps: [
{ id: "a", title: "Input", component: "StepA" },
{ id: "b", title: "Result", component: "StepB" },
],
};
const schemaComponents = { StepA, StepB };
const schemaData = ref({ value: "" });
</script>
Schema: defineComponent
Step components defined with defineComponent and setup returning a render function.
Â
Â
View Source 💻
<template>
<FormWizard
title="Schema: defineComponent"
:schema="schema"
:schema-components="schemaComponents"
v-model="schemaData"
@on-complete="onComplete"
/>
</template>
<script setup>
import { ref, defineComponent, h } from "vue";
import { FormWizard } from "vue3-form-wizard";
import 'vue3-form-wizard/dist/style.css'
const NameStep = defineComponent({
name: "NameStep",
props: { data: Object, updateData: Function },
setup(props) {
return () =>
h("div", [
h("input", {
value: props.data.name,
onInput: (e) => props.updateData({ name: e.target.value }),
}),
]);
},
});
const SummaryStep = defineComponent({
name: "SummaryStep",
props: { data: Object, updateData: Function },
setup(props) {
return () => h("div", [h("p", "Hello, " + (props.data.name || "Guest"))]);
},
});
const schema = {
initialData: { name: "" },
steps: [
{ id: "name", title: "Name", component: "NameStep" },
{ id: "summary", title: "Summary", component: "SummaryStep" },
],
};
const schemaComponents = { NameStep, SummaryStep };
const schemaData = ref({ name: "" });
</script>
Async validation with error message
before-change beforeChange (): boolean | Promise<boolean> can accept a promise that resolves with a boolean. Resolving with a truthy value, will trigger the navigation to next step. Rejecting with a message, will set an internal message that can be handled and displayed if needed.
Â
Â
View Source 💻
<template>
<form-wizard
@on-complete="onComplete"
@on-loading="setLoading"
@on-validate="handleValidation"
@on-error="handleErrorMessage"
shape="circle"
color="gray"
error-color="#e74c3c"
>
<tab-content
title="Personal details"
:before-change="validateAsync"
icon="ti-user"
>
First tab
</tab-content>
<tab-content title="Additional Info" icon="ti-settings">
Second tab
</tab-content>
<tab-content title="Last step" icon="ti-check"> Third tab </tab-content>
<div class="loader" v-if="loadingWizard"></div>
<div v-if="errorMsg">
<span class="error">{{ errorMsg }}</span>
</div>
</form-wizard>
</template>
<script setup>
import { ref } from "vue";
//local registration
import { FormWizard, TabContent } from "vue3-form-wizard";
const loadingWizard = ref(false);
const errorMsg = ref("");
const count = ref(0);
const onComplete = () => {
alert("Yay. Done!");
};
const setLoading = (value) => {
loadingWizard.value = value;
};
const handleValidation = (isValid, tabIndex) => {
console.log("Tab: " + tabIndex + " valid: " + isValid);
};
const handleErrorMessage = (err) => {
errorMsg.value = err;
};
const validateAsync = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (count.value < 1) {
count.value++;
reject(
"This is a custom validation error message. Click next again to get rid of the validation"
);
} else {
count.value = 0;
resolve(true);
}
}, 1000);
});
};
</script>
<style>
@import url("https://unpkg.com/vue3-form-wizard/dist/style.css");
span.error {
color: #e74c3c;
font-size: 20px;
display: flex;
justify-content: center;
}
/* This is a css loader. It's not related to vue-form-wizard */
.loader,
.loader:after {
border-radius: 50%;
width: 10em;
height: 10em;
}
.loader {
margin: 60px auto;
font-size: 10px;
position: relative;
text-indent: -9999em;
border-top: 1.1em solid rgba(255, 255, 255, 0.2);
border-right: 1.1em solid rgba(255, 255, 255, 0.2);
border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
border-left: 1.1em solid #e74c3c;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation: load8 1.1s infinite linear;
animation: load8 1.1s infinite linear;
}
@-webkit-keyframes load8 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes load8 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>