\n\n\n\nKey design decisions worth understanding\n\nAll form data lives in one formData object, separate from UI state (activeStepIndex) — this gives a clean separation between \"what step am I on\" and \"what has the user entered,\" and means the full data set is available at submission regardless of which step it was entered on\nv-show rather than v-if for steps — v-show keeps all steps in the DOM and just toggles CSS visibility, which preserves each step's input state as you navigate back and forth. If you used v-if, Vue would destroy and recreate the DOM elements on each step change, and any local component state not bound to your shared formData object would reset\nA single
wrapping all steps, with only the active step's inputs visible — this way @submit.prevent and native form semantics still work correctly on the final step\n\nMaking it reusable with slots — the pattern for a genuinely reusable multi-step component\nRather than hardcoding each step's markup inside one component, use named slots so the parent controls what each step contains:\n\nvue\n\n\n\n\n\nThis lets any parent component define wildly different content per step (different field sets, different layouts) while reusing the same navigation/state logic — genuinely the right pattern once you have more than one multi-step form in your app.\n\nValidation — gate step transitions, don't just let users click through\nFor a production form, add a check in nextStep() that validates the current step's fields before advancing (rather than trusting the user got everything right) — pairing this with VeeValidate + Zod (covered in earlier Vue validation questions in this conversation) is the standard approach: validate the current step's schema slice before incrementing activeStepIndex.\n\nIf you want this handled for you rather than building it by hand\n\nVueform (covered in the previous \"complex forms\" question) has native step support via / components, including conditional steps and automatic exclusion of hidden-step data from the submission payload\nvue-multi-step-form (npm package) — a ready-made slot-based multi-step component if you want the reusable pattern above without building it yourself\n\nPractical recommendation\n\nSimple, one-off multi-step form → the v-show + shared formData pattern above, hand-built\nMultiple multi-step forms across your app, want reusability → the slot-based component pattern, or the vue-multi-step-form package\nNeed step-level conditional logic (skip a step based on a prior answer) and built-in validation → Vueform"}}}

Prompt

How do I create a multi step form in Vue.js?

Technology · Developer Tools / Developer tools3 observationsLast seen Jul 24, 2026

Latest observation

Jul 24, 2026 · ClaudeWeb search: on

Here's how to build a multi-step form in Vue 3, using the current recommended pattern.

  • The core mechanism: a single index (current step) plus v-show to toggle visibility

vue

<!-- MultiStepForm.vue -->

<script setup>

import { ref } from 'vue'

const activeStepIndex = ref(0)

const totalSteps = 3

const formData = ref({

  • name: '',
  • email: '',
  • street: '',
  • city: '',

})

function nextStep() {

  • if (activeStepIndex.value < totalSteps — 1) activeStepIndex.value++

}

function prevStep() {

if (activeStepIndex.value > 0) activeStepIndex.value--

}

function submit() {

console.log(formData.value)

// POST formData to your API here

}

</script>

<template>

<form @submit.prevent="submit">

<!-- Step indicator -->

<div class="steps">

<div

v-for="n in totalSteps"

:key="n"

  • :class="{ active: n — 1 === activeStepIndex }"

>

{{ n }}

</div>

</div>

<!-- Step 1 -->

<div v-show="activeStepIndex === 0">

<input v-model="formData.name" placeholder="Name" />

<input v-model="formData.email" placeholder="Email" />

</div>

<!-- Step 2 -->

<div v-show="activeStepIndex === 1">

<input v-model="formData.street" placeholder="Street" />

<input v-model="formData.city" placeholder="City" />

</div>

  • <!-- Step 3: review/confirm -->

<div v-show="activeStepIndex === 2">

  • <p>Name: {{ formData.name }}</p>
  • <p>Email: {{ formData.email }}</p>

</div>

<footer>

<button type="button" @click="prevStep" v-if="activeStepIndex > 0">Previous</button>

<button type="button" @click="nextStep" v-if="activeStepIndex < totalSteps - 1">Next</button>

<button type="submit" v-else>Submit</button>

</footer>

</form>

</template>

Key design decisions worth understanding

  • All form data lives in one formData object, separate from UI state (activeStepIndex) — this gives a clean separation between "what step am I on" and "what has the user entered," and means the full data set is available at submission regardless of which step it was entered on

  • v-show rather than v-if for steps — v-show keeps all steps in the DOM and just toggles CSS visibility, which preserves each step's input state as you navigate back and forth. If you used v-if, Vue would destroy and recreate the DOM elements on each step change, and any local component state not bound to your shared formData object would reset

  • A single <form> wrapping all steps, with only the active step's inputs visible — this way @submit.prevent and native form semantics still work correctly on the final step

  • Making it reusable with slots — the pattern for a genuinely reusable multi-step component Rather than hardcoding each step's markup inside one component, use named slots so the parent controls what each step contains:

vue

<!-- MultiStepForm.vue (reusable) -->

<template>

<form @submit.prevent="submitStep">

<div

v-for="(step, index) in steps"

:key="index"

v-show="activeStepIndex === index"

>

<slot :name="'step' + (index + 1)" />

</div>

<button type="submit">Next</button>

</form>

</template>

<script setup>

import { ref } from 'vue'

defineProps(['steps'])

const activeStepIndex = ref(0)

</script>

  • This lets any parent component define wildly different content per step (different field sets, different layouts) while reusing the same navigation/state logic — genuinely the right pattern once you have more than one multi-step form in your app.

  • Validation — gate step transitions, don't just let users click through

  • For a production form, add a check in nextStep() that validates the current step's fields before advancing (rather than trusting the user got everything right) — pairing this with VeeValidate + Zod (covered in earlier Vue validation questions in this conversation) is the standard approach: validate the current step's schema slice before incrementing activeStepIndex.

If you want this handled for you rather than building it by hand

Vueform (covered in the previous "complex forms" question) has native step support via <FormSteps>/<FormStep> components, including conditional steps and automatic exclusion of hidden-step data from the submission payload

  • vue-multi-step-form (npm package) — a ready-made slot-based multi-step component if you want the reusable pattern above without building it yourself

Practical recommendation

Simple, one-off multi-step form → the v-show + shared formData pattern above, hand-built

Multiple multi-step forms across your app, want reusability → the slot-based component pattern, or the vue-multi-step-form package

Need step-level conditional logic (skip a step based on a prior answer) and built-in validation → Vueform

Observation history

See 2 earlier observations

Create a free Obsurfable account to unlock historical responses, model comparisons, and deeper brand intelligence.

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (3 observations for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, and Meta AI.