Logo

dev-resources.site

for different kinds of informations.

How to build a multi step form in Vue

Published at
6/22/2022
Categories
vue
typescript
tailwindcss
daisyui
Author
nikhilhenry
Author
11 person written this
nikhilhenry
open
How to build a multi step form in Vue

Forms and data input are always an essential aspect of any web application. At times, the web app may require to present the user with a series of inputs. Multi-step forms help achieve this goal with a stellar and distinct user experience. Today we'll be building a multi-step form in vue using typescript and Tailwindcss and daisyUI, both of which compile down to plain css hence avoiding any increase in bundle size.

You can checkout the finished product here or take a look at the source code here.

Setup

The project was scaffolded using vite with the vue-ts template. Run the command below and select vue-ts as the template from the vue option.

npm create vite
Enter fullscreen mode Exit fullscreen mode

Installation instruction for tailwindcss can be found here. Their docs are brilliant, so you will have a better time over there ๐Ÿ˜‡.

To install daisyUI run:

npm i daisyUI --save-dev
Enter fullscreen mode Exit fullscreen mode

Then add daisyUI to your tailwind.config.js files:

module.exports = {
  //...
  plugins: [require("daisyui")],
}
Enter fullscreen mode Exit fullscreen mode

Form steps

Each section of the multi-step form is its own individual component. This allows the implementation to be modular so that the individual elements can manage their own data binding and logic whilst limiting concern from other components.

Below are a few sample steps (styled with tailwind and daisyUI), but feel free to experiment with your own.

  1. Step 1 โ†’ ./src/components/Step1.vue
<template>
    <div class="form-control w-full">
      <label class="label">
        <span class="label-text">What is your name?</span>
      </label>
      <input type="text" placeholder="Type here" class="input input-bordered w-full" />
    </div>
    <div class="form-control max-w-xs pt-4">
      <label class="cursor-pointer label">
        <span class="label-text">I am awesome</span>
        <input type="checkbox" checked="checked" class="checkbox" />
      </label>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode
  1. Step 2 โ†’ ./src/components/Step2.vue
<template>
    <div class="form-control w-full">
      <label class="label">
        <span class="label-text">Pick the best fantasy franchise</span>
      </label>
      <select class="select select-bordered">
        <option disabled selected>Pick one</option>
        <option>Star Wars</option>
        <option>Harry Potter</option>
        <option>Lord of the Rings</option>
        <option>Planet of the Apes</option>
        <option>Star Trek</option>
      </select>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode
  1. Step 3 โ†’ ./src/components/Step3.vue
<template>
    <div class="form-control w-full flex items-center justify-center">
    <h2 class="text-xl py-3">Rate this tutorial</h2>
        <div class="rating rating-lg">
          <input type="radio" name="rating-9" class="rating-hidden" />
          <input type="radio" name="rating-9" class="mask mask-star-2" />
          <input type="radio" name="rating-9" class="mask mask-star-2" checked />
          <input type="radio" name="rating-9" class="mask mask-star-2" />
          <input type="radio" name="rating-9" class="mask mask-star-2" />
          <input type="radio" name="rating-9" class="mask mask-star-2" />
        </div>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Displaying steps and step progress

This is where the powerful class styles of daisyUI come in handy to elegantly style the progress indicator with a single class definition.

./src/components/MultiStepForm.vue โ†’ template section

<template>
    <div class="w-6/12">
        <ul class="steps min-w-full">
            <li v-for="(stepText,index) in props.steps" class="step" :class="index<=step ? 'step-primary' : ''">
                {{stepText}}
            </li>
        </ul>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

./src/components/MultiStepForm.vue โ†’ script section

<script lang="ts" setup>
import {ref} from "vue"

let step = ref(0);
const props = defineProps<{
    steps:string[],
}>()
</script>
Enter fullscreen mode Exit fullscreen mode

Now, we will import our new component into the App.vue file

./src/App.vue

<template>
  <div class="flex flex-col items-center justify-center h-screen">
    <MultiStepForm :steps="['Personal information ๐Ÿ‘ถ','Series ๐Ÿ“บ','Feedback ๐ŸŒŸ']"/>
  </div>
</template>

<script lang="ts" setup>
 import MultiStepForm from "./components/MultiStepForm.vue" 
</script>
Enter fullscreen mode Exit fullscreen mode

The page should now be looking similar to this.

progress bar

Accepting step components as props

We can start accepting vue components as props for our MultiStepForm component with typesafety thanks to the power of typescript (in particular type inference).

./src/components/MultiStepForm.vue โ†’ script section

<script lang="ts" setup>
// ....
import Step1 from "./Step1.vue"
// ...
const props = defineProps<{
    forms:typeof Step1[], // inferring the component type of one of our steps 
    steps:string[],
}>()
</script>
Enter fullscreen mode Exit fullscreen mode

Rendering components within the form

We can now render the components we have received as props using vue's special built-in element: component.

./src/components/MultiStepForm.vue โ†’ template section

<template>
<!-- ...progress indicator... -->
<div class="py-3"></div> 
<form @submit.prevent class="min-w-full px-6"> 
  <component :is="props.forms[step]"></component>
    <div class="py-4"></div> 
    <div class="flex justify-end">
      <button class="btn btn-ghost" type="button" v-if="step!==0" @click="step--">Back</button>
      <button class="btn btn-primary" type="submit" v-if="step!==props.steps.length-1">Next</button>
      <button class="btn btn-primary" type="submit" v-if="step==props.steps.length-1">Submit</button>
    </div>
</form>
</div>
</template>
Enter fullscreen mode Exit fullscreen mode

Add next and previous step logic

To traverse through our array of components, we simply need to increment the value of our reactive variable step. We also need to make sure our back, next and submit buttons are only active at certain conceptual environments.

./src/components/MultiStepForm.vue โ†’ script section

<template>
<!-- within the form -->
  <div class="py-4"></div> 
  <div class="flex justify-end">
    <button class="btn btn-ghost" type="button" v-if="step!==0" @click="step--">Back</button>
    <button class="btn btn-primary" type="submit" v-if="step!==props.steps.length-1">Next</button>
    <button class="btn btn-primary" type="submit" v-if="step==props.steps.length-1">Submit</button>
  </div>
<!-- within the form -->
</template>
Enter fullscreen mode Exit fullscreen mode

Handle final submit

We will now pass in and accept a submitFunction as a prop to our component to execute once all the steps have been completed.

./src/components/MultiStepForm.vue โ†’ script section

<script lang="ts" setup>
// ...
const props = defineProps<{
  forms: typeof Step1[];
  steps: string[];
  submitAction: () => void;
}>();

const formAction = () => {
  if (step.value !== props.steps.length - 1) return step.value++;
  props.submitAction();
};
// ...
</script>
Enter fullscreen mode Exit fullscreen mode

./src/App.vue

<template>
  <div class="flex flex-col items-center justify-center h-screen">
    <MultiStepForm :forms="forms" :steps="['Personal information ๐Ÿ‘ถ','Series ๐Ÿ“บ','Feedback ๐ŸŒŸ']" 
      :submit-action="submitAction"
    />
  </div>
</template>

<script lang="ts" setup>
 import MultiStepForm from "./components/MultiStepForm.vue" 
 import Step1 from "./components/Step1.vue"
 import Step2 from "./components/Step2.vue"
 import Step3 from "./components/Step3.vue"

 const forms = [Step1,Step2,Step3]
 const submitAction = () => {console.log("submitting form...")}
</script>
Enter fullscreen mode Exit fullscreen mode

Summary

There we have it, a type-safe implementation of a multi-step form in vue with an elegant design and UX through tailwindcss and daisyUI. For a quick reference you can also checkout the codesandbox below ๐Ÿ‘‡.

You can find the source code on GitHub. Be sure to give the project a start if you find this tutorial helpful!

daisyui Article's
22 articles in total
Favicon
Creating a To-Do app with HTMX and Django - Part 3: Creating the frontend and adding HTMX
Favicon
What is DaisyUI? Advantages, Disadvantages, and FAQโ€™s
Favicon
DaisyUI: CSS Components for Tailwind
Favicon
Discover the beauty of simplicity with ๐——๐—ฎ๐—ถ๐˜€๐˜†๐—จ๐—œ! ๐Ÿš€
Favicon
daisyUI adoption guide: Overview, examples, and alternatives
Favicon
Getting started with Tailwind + Daisy UI in Angular 18
Favicon
๐ŸŽ‰ My First Shot at a Vue Library: Introducing masc-vue!๐ŸŒŸ
Favicon
Binary Duel, front-end in 2 weeks with Svelte and DaisyUI
Favicon
My Journey with Svelte: From Vue to Svelte and the Joy of Pure JS Libraries
Favicon
Stop flickering theme after page refresh in Sveltekit and Daisy UI
Favicon
DevTips โ€“ DaisyUi (Rating, Carousel)
Favicon
Building Contact Form in React with DaisyUI and Tailwind CSS
Favicon
Create an Emoji Selector for Next.js Forms using Tailwind + DaisyUI
Favicon
Install TailwindCSS and DaisyUI CSS Plugin with Angular
Favicon
Configurar DaisyUI con Vue3
Favicon
Setting up DaisyUI, Tailwind, Vue and Vite
Favicon
How to build a multi step form in Vue
Favicon
DaisyUIใงใƒ‡ใ‚ณใฃใŸPhoenixใ‚ขใƒ—ใƒชใ‚’Fly.ioใซใƒ‡ใƒ—ใƒญใ‚คใ—ใฆๆฅฝใ—ใ‚€(2022ๅนด)
Favicon
Get started creating beautiful designs with daisyUI
Favicon
Quick Prototyping with Tailwind and DaisyUI
Favicon
Adding Tailwind and Daisy UI to SvelteKit
Favicon
Sveltekit web gallery app

Featured ones: