In this snippet we will learn how to validate forms in Vue 3 applications through using the Vee-validate library.
Installing Vee-validate
I suppose that you have Vue 3 app already installed, so let’s go a head and install vee-validate:
npm i vee-validate --save
Once installed we will create a simple form to experiment validation with, then we will update this form to use vee-validate related components to apply the validations.
Create a vue component:
components/SubmitPostForm.vue
<script setup> const handleSubmit = () => { } </script> <template> <form novalidate @submit.prevent="handleSubmit"> <h3>Submit Post</h3> <p> <label>Title</label> <input type="text" name="title" /> </p> <p> <label>Content</label> <textarea name="content" rows="5" cols="30"></textarea> </p> <p> <label>Tags</label> <select name="tags" multiple> <option value=""></option> <option value="news">News</option> <option value="world">World</option> <option value="politics">Politics</option> <option value="economy">Economy</option> <option value="technology">Technology</option> </select> </p> <p> <button type="submit" name="submit">Submit</button> </p> </form> </template> <style scoped> p{ display: flex; justify-content: center; align-items: center; } label { margin-inline-end: 5px; } input[type=text], textarea { font-size: 23px; padding: 7px 6px; } select { height: 100px; } </style>
This component contains a simple form to submit a post. In the <form> tag i added the novalidate attribute to prevent the default html form validation and added the @submit event so that we can capture form submission.
Note that i am using Vue 3 composition api through using <script setup>.
Applying Validations
To start applying validations first we will modify the form and inputs to use Vee-validate related components like <Field /> and <Form />, so let’s refactor our form to use these components:
<script setup> import {Form, Field, ErrorMessage} from "vee-validate"; const handleSubmit = (values) => { console.log('submitted', values); } const validateRequired = (value) => { if (!value) { return 'The field is required'; } return true; } </script> <template> <Form @submit="handleSubmit"> <h3>Submit Post</h3> <p> <label>Title</label> <div class="input"> <Field type="text" name="title" :rules="validateRequired" /> <ErrorMessage name="title" /> </div> </p> <p> <label>Content</label> <div class="input"> <Field name="content" as="textarea" rows="5" cols="30" :rules="validateRequired"></Field> <ErrorMessage name="content" /> </div> </p> <p> <label>Tags</label> <div class="input"> <Field name="tags" as="select" multiple :rules="validateRequired"> <option value=""></option> <option value="news">News</option> <option value="world">World</option> <option value="politics">Politics</option> <option value="economy">Economy</option> <option value="technology">Technology</option> </Field> <ErrorMessage name="tags" /> </div> </p> <p> <button type="submit" name="submit">Submit</button> </p> </Form> </template>
This is the refactored form after applying vee-validate components. At first i imported the <Form>, <Field>, and <ErrorMessage> components from vee-validate. Next i replaced each input with the <Field/> component.
The <Field/> component renders a any kind of input. In the simplest case it renders a text input, however you can customize it to render different inputs, for example to render an email or password inputs you can specify the type attribute like so:
<Field type="password" name="password" /> <Field type="email" name="email" />
Also the <Field/> components renders textarea’s or select fields by specifying the “as” prop like in the code above:
<Field name="content" as="textarea" rows="5" cols="30"></Field>
<Field name="tags" as="select" multiple>
To apply validations in this form, the <Field/> component accepts a “rules” prop. The “rules” prop accept a set of validation rules. Each rule is a function to takes the field value and returns a boolean that indicates whether the validation passed or failed as in the validateRequired() rule above:
const validateRequired = (value) => { if (!value) { return 'The field is required'; } return true; }
The i assigned the validateRequired rule to the :rules prop in each field. To display the error message in case validation failed vee-validate provides us the <ErrorMessage /> component and it takes the field name as a prop and renders the error message.
If everything goes well and no error occurs the @submit event triggers and executes the handleSubmit() function. I updated the handleSubmit to accept an argument which is the form values. vee-validate extracts the form values from inputs and adds it to the submit handler in this case handleSubmit which we can access them easily and send them to the server.
Now you can test this form and try to fill the values and hit submit, you will see the values logged to the console.
vee-validate built-in rules
Vee-validate comes with built-in rules for common form validations like required, email validations, min, max, etc, so you don’t have to write these validations from scratch. These rules in a separate package, so let’s install it first:
npm install @vee-validate/rules
Also install vee-validate i18n for localized error messages:
npm install @vee-validate/i18n
Let’s apply the built-in rules with our form:
<script setup> import {Form, Field, ErrorMessage, defineRule, configure} from "vee-validate"; import { required, min, length } from "@vee-validate/rules"; import { localize } from '@vee-validate/i18n'; import en from '@vee-validate/i18n/dist/locale/en.json'; configure({ generateMessage: localize({ en }), }); defineRule('required', required); defineRule('min', min); defineRule('length', length); const handleSubmit = (values) => { console.log('submitted', values); } </script> <template> <Form @submit="handleSubmit"> <h3>Submit Post</h3> <p> <label>Title</label> <div class="input"> <Field type="text" name="title" rules="required" /> <ErrorMessage name="title" /> </div> </p> <p> <label>Content</label> <div class="input"> <Field name="content" as="textarea" rows="5" cols="30" :rules="{required: true, min: 20}"></Field> <ErrorMessage name="content" /> </div> </p> <p> <label>Tags</label> <div class="input"> <Field name="tags" as="select" multiple rules="required|length:3"> <option value=""></option> <option value="news">News</option> <option value="world">World</option> <option value="politics">Politics</option> <option value="economy">Economy</option> <option value="technology">Technology</option> </Field> <ErrorMessage name="tags" /> </div> </p> <p> <button type="submit" name="submit">Submit</button> </p> </Form> </template>
To use the built-in rules you have to import the rules from @vee-validate/rules package we just installed. In our case i imported only the required, min and length rules. You can check all the available rules at vee-validate docs.
If you prefer to import all the rules instead you can do so with this code:
import { defineRule } from 'vee-validate'; import { all } from '@vee-validate/rules'; Object.entries(all).forEach(([name, rule]) => { defineRule(name, rule); });
Next i imported the localize function and the localized messages, in our case the form locale is english so i imported en locale:
import en from '@vee-validate/i18n/dist/locale/en.json';
If the form will be rendered in another locale, for example arabic you have to import the arabic messages as well:
import ar from '@vee-validate/i18n/dist/locale/ar.json';
To register the localized messages i invoked the configure function. The configure function register a message generator.
Then i called vee-validate defineRule() function which register a rule globally. This function accepts the rule id and the built-in validator. The rule id later can be applied to form inputs.
Finally i applied to registered rules to each input like so:
<Field type="text" name="title" rules="required" />
In case there is multiple rules, you can separate them by pipe |
<Field name="tags" as="select" multiple rules="required|length:3">
Instead of using rules as strings, you can also use them as Javascript objects:
<Field name="content" as="textarea" rows="5" cols="30" :rules="{required: true, min: 20}"></Field>
vee-validate with yup
vee-validate can be integrated with schema based validation libraries like yup or zod packages. Let’s see how to install and use yup with vee-validate.
Let’s install the yup package:
npm i --save yup
Next import this package and define the schema like so:
<script setup> import {Form, Field, ErrorMessage} from "vee-validate"; import * as yup from 'yup'; const schema = yup.object({ title: yup.string().required(), content: yup.string().required(), tags: yup.array().required() } ); const handleSubmit = (values) => { .... } </script>
To use and apply the schema, the <Form /> component accepts the :validation-schema prop:
<Form @submit="handleSubmit" :validation-schema="schema" >
Now try the form again you will see that validations appear as expected. To learn more about Yup validations head over the yup docs.