How to validate forms in Vue 2 with vee-validate

In this tutorial, we will learn how to validate a form in Vue 2 with vee-validate. We will also explore the benefits of using vee-validate and how it can be integrated into your Vue 2 project. Vee-validate is a validation library for Vue.js that helps developers to quickly validate input fields in their application and provides tools for error handling and display. It has a lot of built-in validators and it's really easy to use them in our project. We will show you a step-by-step guide to getting started with frontend validation in VueJs.

Step 1 - Installing the package

npm install vee-validate@3 --save

Step 2 - Setup the vee-validate

In the /src folder create a vee-validate folder containing the index.js file. This is where we set up all the vee-validate and import its components and the rules for validation.

/src/vee-validate/index.js

import Vue from 'vue';
import { ValidationObserver, ValidationProvider, extend, localize } from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';

function loadLocale(code) {
    return import(`vee-validate/dist/locale/${code}.json`).then(locale => {
        localize(code, locale);
    });
}
// Set default language
loadLocale('en');

// Install rules and localization
Object.keys(rules).forEach(rule => {
    extend(rule, rules[rule]);
});

// Install components globally
Vue.component('ValidationObserver', ValidationObserver);
Vue.component('ValidationProvider', ValidationProvider);

Step 3 - Adding the vee-validate to Vue app

To integrate our vee-validate configuration just add the require vee-validate to the main.js of your Vue application. It will then register all the code in main.js.

/src/main.js

import Vue from 'vue';
import App from './App.vue';
import router from './routes';

Vue.config.productionTip = false;

require('./vee-validate');

new Vue({
    router,
    render: h => h(App)
}).$mount('#app')

Step 4 - Setup the pages with route

Set up pages to use the validate with input. We will create a route that redirects to a page to show the example of our validation.

/src/routes/index.js

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const router = new VueRouter({
    mode: "history",
    routes: [
        {
            path: '/',
            name: '/',
            component: () => import('@/pages/InputValidation')
        }
    ]
});

export default router;

Step 5 - Using the validation

The ValidationObserver act as an observer that tracks all the ValidationProvider and gives a way for us to trigger and know if the validation is passed or not. White ValidationProvider is the one who controls the rules to validate itself and handle error messages, etc. Then you will see when the submit button is clicked error message will appear if any validation is invalid or valid and pass through to the submit method and alert success.

/src/pages/InputValidation.vue

<template>
    <ValidationObserver v-slot="{ handleSubmit }">
        <ValidationProvider name="E-mail" rules="required|email" v-slot="{ errors }">
            <label for="email" class="d-block">Email</label>
            <input v-model="form.email" type="email">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Username" rules="min:8|max:12" v-slot="{ errors }">
            <label for="Username" class="d-block">Username</label>
            <input v-model="form.username" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Password" rules="required|min:8" vid="password" v-slot="{ errors }">
            <label for="Password" class="d-block">Password</label>
            <input v-model="form.password" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>

        <button @click="handleSubmit(submit)">Submit</button>
    </ValidationObserver>
</template>

<script>
export default {
    data() {
        return {
            form: {
                email: '',
                username: '',
                password: ''
            }
        }
    },
    methods: {
        submit() {
            alert('Form has been submitted!');
        }
    }
}
</script>

<style scoped>
    .d-block {
        display: block;
    }
</style>

Bonus - Customize the vee-validate rule

Vee-validate also allows us to customize the rule for validation, here we show an example to customize and register the password rule which will validate the both min and max length and we need to set the specific message for the validation as well.

/src/vee-validate/index.js

import Vue from 'vue';
import { ValidationObserver, ValidationProvider, extend, localize } from 'vee-validate';
import * as rules from 'vee-validate/dist/rules';

function loadLocale(code) {
    return import(`vee-validate/dist/locale/${code}.json`).then(locale => {
        localize(code, locale);
    });
}
// Set default language
loadLocale('en');

// Install rules and localization
Object.keys(rules).forEach(rule => {
    extend(rule, rules[rule]);
});

// Custom rules
extend("password", {
    validate: (value) => {
        if (value === null || value === undefined || value === '') {
            return {
                valid: false
            };
        }

        return {
            valid: value.length >= 8 && value.length <= 12,
        };
    },
    message: 'The {_field_} field must be a valid password and minimum character of 8 and maximum character of 12'
});

// Install components globally
Vue.component('ValidationObserver', ValidationObserver);
Vue.component('ValidationProvider', ValidationProvider);

Bonus - Using the custom validate

Just like the default rules of vee-validate, the customized rule can use by calling the name of the rule in the rules attribute.

/src/pages/InputValidation.vue

<template>
    <ValidationObserver v-slot="{ handleSubmit }">
        <ValidationProvider name="E-mail" rules="required|email" v-slot="{ errors }">
            <label for="email" class="d-block">Email</label>
            <input v-model="form.email" type="email">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Username" rules="min:8|max:12" v-slot="{ errors }">
            <label for="Username" class="d-block">Username</label>
            <input v-model="form.username" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Password" rules="required|min:8" vid="password" v-slot="{ errors }">
            <label for="Password" class="d-block">Password</label>
            <input v-model="form.password" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>

        <ValidationProvider name="Password custom" rules="password" v-slot="{ errors }">
            <label for="Password" class="d-block">Password custom</label>
            <input v-model="form.password_custom" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>

        <button @click="handleSubmit(submit)">Submit</button>
    </ValidationObserver>
</template>

<script>
export default {
    data() {
        return {
            form: {
                email: '',
                username: '',
                password: '',
                password_custom: ''
            }
        }
    },
    methods: {
        submit() {
            alert('Form has been submitted!');
        }
    }
}
</script>

<style scoped>
    .d-block {
        display: block;
    }
</style>

Bonus - Cross-field validate

Cross-field validation is a process of validating between two or more fields for example password confirmation, date comparison and etc. Below is an example of how to cross-field validate with the password field and password confirmation field. The vid is what is used to declare the reference to identify the target field.

<template>
    <ValidationObserver v-slot="{ handleSubmit }">
        <ValidationProvider name="E-mail" rules="required|email" v-slot="{ errors }">
            <label for="email" class="d-block">Email</label>
            <input v-model="form.email" type="email">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Username" rules="min:8|max:12" v-slot="{ errors }">
            <label for="Username" class="d-block">Username</label>
            <input v-model="form.username" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>
        <ValidationProvider name="Password" rules="required|min:8" vid="password" v-slot="{ errors }">
            <label for="Password" class="d-block">Password</label>
            <input v-model="form.password" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>

        <ValidationProvider name="Password confirmation" rules="confirmed:password" v-slot="{ errors }">
            <label for="Password" class="d-block">Password custom</label>
            <input v-model="form.password_confirmation" type="text">
            <span class="d-block">{{ errors[0] }}</span>
        </ValidationProvider>

        <button @click="handleSubmit(submit)">Submit</button>
    </ValidationObserver>
</template>

<script>
export default {
    data() {
        return {
            form: {
                email: '',
                username: '',
                password: '',
                password_confirmation: ''
            }
        }
    },
    methods: {
        submit() {
            alert('Form has been submitted!');
        }
    }
}
</script>

<style scoped>
    .d-block {
        display: block;
    }
</style>