Rootstrap Get Started →

Insights

How to Implement Form Validation with Vuelidate in a Vite + Vue Project

How to Implement Form Validation with Vuelidate in a Vite + Vue Project

Vuelidate is an adaptable and user-friendly form validation solution designed for Vue.js. In this tutorial, I will walk you through the process of installing and configuring Vuelidate within a Vite + Vue project.

Step 1: Setting Up a Vite + Vue Project

To begin, let's initiate a new project using Vite. We'll utilize "create-vite," a tool that enables rapid generation of project templates for popular frameworks:

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Once the project is generated, navigate to its directory:

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Step 2: Installing Vuelidate

Vuelidate 2.x is compatible with Vue 3. To install Vuelidate, we need to include the following dependencies:

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Step 3: Configuring Vuelidate for Form Validation

In earlier versions of Vuelidate (0.x), we could apply it globally across our application using the Vuelidate plugin. However, this approach has been removed in the latest versions. The current approach involves using the useValidate function within the component we intend to validate.

Let's proceed by creating a basic registration form with fields for name, email, password, and password confirmation. We'll place this form in a new component named ‘RegisterForm.vue’ within the components folder.

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

The fields are validated based on the rules defined in the "rules" object. If any field fails the validations, an error message is displayed associated with the corresponding field. When the form is submitted, the "handleSubmit" function validates the fields using Vuelidate and shows an alert with the result of the form submission.

Code Breakdown

Importing Modules:
/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

The previous code snippet begins by importing essential functionalities from both Vue and Vuelidate. It employs the reactive and computed elements to enable dynamic updates and efficient computations. The code further utilizes the useVuelidate function from the Vuelidate library. Additionally importing some validators that we will use later in our validation rules.

Reactive Form Object:
/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Creating a form object using Vue's reactive function, serving as a container for the values of form fields. This object is initially populated with empty strings, and as users engage with the form on the user interface and input data, the content of these fields within the object dynamically updates to reflect the entered values.

Validation Rules:
/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

We created a rules object which will serve as validation guidelines for each form field. These rules are wrapped within a function that keeps them responsive, allowing them to update when related fields change. In this order, specific requirements are defined like this: the name field must have a value, the email field should contain a valid email address, the password field must be at least 8 characters long, and the confirmPassword field needs to match the value entered in the password field.

Step 4: Testing the Form

Integrate the form into your main component (App.vue) for testing purposes:

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Now, you can launch the Vite development server to test the form and its validations:

/* App.vue */
<template>
  <RegisterForm />
</template>

<script>
import RegisterForm from './components/RegisterForm.vue'
</script>

/* RegisterForm.vue */
<template>
  <!-- The form uses @submit.prevent to capture the form submission event and execute the "handleSubmit" function. -->
  <form @submit.prevent="handleSubmit">
    <div class="field-container">
      <!-- Input field for name -->
      <label for="name">Name:</label>
      <input v-model="form.name" type="text" id="name" placeholder="Insert your name">
      <!-- Display an error message if the name field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.name.$error">{{ v$.name.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for email -->
      <label for="email">Email:</label>
      <input v-model="form.email" type="text" id="email" placeholder="Insert your email">
      <!-- Display an error message if the email field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.email.$error">{{ v$.email.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for password -->
      <label for="password">Password:</label>
      <input v-model="form.password" type="password" id="password" placeholder="Insert your password">
      <!-- Display an error message if the password field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.password.$error">{{ v$.password.$errors[0].$message }}</span>
    </div>
    <div class="field-container">
      <!-- Input field for confirming the password -->
      <input v-model="form.confirmPassword" type="password" placeholder="Confirm password">
      <!-- Display an error message if the password confirmation field doesn't pass the validations defined in "rules" -->
      <span v-if="v$.confirmPassword.$error">{{ v$.confirmPassword.$errors[0].$message }}</span>
    </div>
    <!-- Button to submit the form -->
    <button type="submit">Sign in</button>
  </form>
</template>

<script setup>
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

// Create a reactive "form" object to store the values of the form fields
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

// Define validation rules for each form field using "computed"
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})
// Use the "useVuelidate" function to perform form validation
const v$ = useVuelidate(rules, form)

// Function to handle form submission
async function handleSubmit() {
  // Validate the form fields
  const result = await v$.value.$validate()
  if (!result) {
    // If there are errors in the form, show an alert indicating that the form is invalid
    alert('The form has errors')
    return
  }
  // If the form is valid, perform some action with the form data
  alert('Form submitted successfully')
}
</script>

<style lang="css">
form {
  width: 400px;
  margin: 0 auto;
  background-color: #f1f1f1;
  padding: 30px;
  border-radius: 20px;
}

div.field-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 10px;
  height: 118px;
}

label {
  text-align: left;
}

input {
  display: block;
  box-sizing: border-box;
  border: none;
  outline: none;
  border-bottom: 1px solid #ddd;
  border-radius: 20px;
  font-size: 1em;
  padding: 20px;
  margin: 10px 0 5px 0;
  width: 100%;
}

span {
  color: red;
  font-size: 0.8em;
  text-align: left;
}

button {
  background-color: #3498db;
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  color: white;
}
</style>


/* changeDirectory.bash */
cd my-vue-app

/* computedRules.js */
const rules = computed(() => {
  return {
    name: { required }, // Name is required
    email: {
      required, // Email is required
      email // Must be a valid email address
    },
    password: {
      required, // Password is required
      minLength: minLength(8) // Password must have at least 8 characters
    },
    confirmPassword: {
      required, // Password confirmation is required
      sameAs: sameAs(form.password) // Must match the value of the entered password
    }
  }
})

/* createVite.bash */
yarn create vite my-vue-app --template vue

/* imports.js */
import { reactive, computed } from 'vue'
import { useVuelidate } from '@vuelidate/core'
import { minLength, required, email, sameAs } from '@vuelidate/validators'

/* installVuelidate.bash */
yarn add @vuelidate/core @vuelidate/validators

/* reactiveForm.js */
const form = reactive({
  name: '',
  email: '',
  password: '',
  confirmPassword: ''
})

/* runProject.bash */
npm run dev

Empty Fields & Error Messages

Password Validation
Successful Validation

Step 5: Conclusion and next steps

With this setup, you can effectively assess form validation using Vuelidate within your Vite + Vue project. Remember that Vuelidate offers a variety of validators beyond those covered here. For a complete list of validators, refer to the official documentation. If you're up for a challenge, you can even create your own custom validators.

Happy coding!

← All insights