# Validating the Contact Form with Vuelidate | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)

**Author:** Ndianabasi Udonkang  
**Published:** 2021-09-13

In this lesson, we will learn how to carry out proper and performant form validations for the contact-creation form of our Google Contacts clone app.

---

## Tags

- [JavaScript](/llms/technical-blog/tag/javascript.md)
- [Vuejs](/llms/technical-blog/tag/vuejs.md)
- [Adonisjs](/llms/technical-blog/tag/adonisjs.md)
- [Quasar Framework](/llms/technical-blog/tag/quasar-framework.md)

## Part of Series: [Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)

This article is part of the **[Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)** series.

### Articles in this Series:

- [Introduction to Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vuejs (Quasar Framework)](/llms/technical-blog/article/xe5mlxg43s9rpx1pxsvfdp3r/introduction-to-full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vuejs-quasar-framework.md)
- [Workspace Setup | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fo8str8yjpkz5dygxg875bpw/workspace-setup-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Frontend Overview | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/nxgmng6fhf1cwjk30coflro2/frontend-overview-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [The Left Drawer | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/m6fagvy2il5h65llxxbbl9os/the-left-drawer-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improve The Header | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/jjwbxcg450jmkkcrrmkmwvwj/improve-the-header-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [New Contact Form Design | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/ebguwpkntk3empakunrki4dq/new-contact-form-design-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- **Validating the Contact Form with Vuelidate | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)** (Current Article)
- [Designing the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/h2p1x1z406ib38zu2dci3jur/designing-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table (Part 2) | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/j0iscua9gg8ekuz8ifh6chpn/designing-the-contacts-table-part-2-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Refactoring the Main Layout | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/uktmlqsivwtr3cvnpld59k9q/refactoring-the-main-layout-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improving User Experience with the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/dzs812auf61ej7qu2cg726dh/improving-user-experience-with-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contact Details Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/kf9pz97n63fvgfrcy5viwzrg/designing-the-contact-details-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Creating the Contact Edit Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fmr90prj84c9zn0633jc368g/creating-the-contact-edit-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [How Software Backends Work | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/aca8fcrghz70nn1wqg3uzbum/how-software-backends-work-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up The Backend | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fxb49fmz9ljbwrimbnnzudfv/setting-up-the-backend-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Why Choose the AdonisJs Framework? | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/lnhm5oudx34yo2869gmcms37/why-choose-the-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Our API Server with AdonisJs Framework | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/x1jgmet84aqj9qggu5mulkh0/setting-up-our-api-server-with-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Postman with the API Server | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/v4qehksislosmuy92qgjr4ct/setting-up-postman-with-the-api-server-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [The Model-View-Controller Design Pattern in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/h5wqql65iejop5xrplujw458/the-model-view-controller-design-pattern-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Create Column Definitions & Insert New Contacts | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/asc2emsho4u73r6jb2gxjddw/create-column-definitions-and-insert-new-contacts-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Data Validation & Sanitisation with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/bcey0ac7m5sbs9hqy1h824sl/data-validation-and-sanitisation-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Creating a Middleware and Updating a Contact | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/oqxzmtgfy1r3oewpnedc7wf6/creating-a-middleware-and-updating-a-contact-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Fetching and Deleting a Contact with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/p6bh0ponq43u3vi6o8uww84x/fetching-and-deleting-a-contact-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Using Model Factories and Seeders in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cfotveuxbgjtdp12l6pzcb2y/using-model-factories-and-seeders-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Registering a Vuex Module | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cw96ewnzujgsz2e2garhbuzb/creating-and-registering-a-vuex-module-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Connecting UI Components to the Vuex Store | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fdyqp17yehbnqrjvjvab4b5b/connecting-ui-components-to-the-vuex-store-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Connecting the Frontend to the API Server | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fk4262359gg3g7f0jn95fn9b/connecting-the-frontend-to-the-api-server-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Updating Contacts From the Frontend | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/t15w2s3ao9jo74l96stjo7z6/creating-and-updating-contacts-from-the-frontend-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Uploading Files and Creating Avatars for Contacts With AdonisJS and Axios](/llms/technical-blog/article/hg9ilaubqesm8angg0rhrjp9/uploading-files-and-creating-avatars-for-contacts-with-adonis-js-and-axios.md)
- [Deleting a Contact with AdonisJS and Vuejs](/llms/technical-blog/article/zdr4pxmgcax2dsfnwimhty0g/deleting-a-contact-with-adonis-js-and-vuejs.md)

---

## Article Content

In this lesson, we will learn how to carry out proper and performant form validations for the contact-creation form of our Google Contacts clone app. The form validations will be done with the help of the beautiful and eloquent [`Vuelidate`](https://vuelidate-next.netlify.app/) validation library for Vue.js applications.

At the end of this lesson, your form validation should work like the video below and your should understand all the concepts by reading the entire lesson.

%\[https://vimeo.com/604626475]

Start by creating a new branch of your project:

```bash
# Make sure you are within your project
git checkout -b 04-validate-new-contact-creation-form
```

### Setup

1. Let's install the Vuelidate library:

   ```bash
   ```

# Make sure you are in the root directory of your project, then:

cd ui # change into the `ui` directory
yarn add @vuelidate/core @vuelidate/validators # install the packages for Vuelidate

````

2. If you do not have the Vue.js devtools extension for your browser, please install one. Chrome and Microsoft Edge can use the same extension. Visit https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg to download and install. We will be using the Vue.js devtools to learn how to analyse the validation errors in the Vue.js application.
3. Open `ui/.eslintrc.js`. Add the following after Line 85:

```diff
    "prefer-promise-reject-errors": "off",
+    "func-names": "off",
+    "no-console": "off",
+    "object-curly-newline": "off",
+    "comma-dangle": "off",
+    "no-useless-escape": "off",

    // TypeScript
    quotes: ["warn", "double", { avoidEscape: true }],
````

This will remove some of the unnecessary linting errors. Please refer to  [this snapshot](https://github.com/ndianabasi/google-contacts/blob/8b8c8c3b92d7db6de54f3c3d3c9755016db2b043/ui/.eslintrc.js#L86-L91)  as reference.

4. Open `ui/quasar.conf.js`. We will add the `Notify` plugin for in-app notifications. Update the `framework` > `config` property by adding `notify: { position: "top" }`. And update `framework` > `plugins` to `plugins: ["Notify"]`. Please refer to this  [snapshot of the file](https://github.com/ndianabasi/google-contacts/blob/4b18e1e415530b4674aa4ce586697d99d0cd0904/ui/quasar.conf.js#L86-L106) .

### Opening the Vue.js Devtools

The Vue.js devtools is extension accessed within the Chrome devtools. Within your Google Contacts clone app, click the Menu button (top-right "|" icon) > More tools > Developer tool. Or press `CTRL+Shift+I`. You will see `Vue` as a tab on the top of the Chrome DevTools. Switch to the tab when you want to inspect your Vue.js components or events or Vuex mutation/state/getters.

### Recommended reads

It is highly recommended that you study the `Vuelidate` docs at https://vuelidate-next.netlify.app/. The docs is short so you should be done within one hour. You could choose to study after completing this lesson so that you can relate the concepts in the docs with what you have learnt here.

### Validating the `New Contact` Form

Open `ui/src/pages/contacts/CreateContact.vue`. Open the [snapshot ](https://github.com/ndianabasi/google-contacts/blob/8b8c8c3b92d7db6de54f3c3d3c9755016db2b043/ui/src/pages/contacts/CreateContact.vue) of the file. Copy the entire content of the snapshot into `CreateContact.vue`. I will explain all the changes.

Beginning from the `script` section. The validation starts by importing the validation packages from `Vuelidate`:

```ts
import useVuelidate from "@vuelidate/core";
import { required, email, url, helpers, integer } from "@vuelidate/validators";
```

The first import statement brings in the `useVuelidate` composable function since we will be using the validation library within the `setup` function (i.e. composition API). The second import statement imports the built-in validators which will use to validate our fields.

In Line 69, a custom `phoneNumberValidator` validator is defined via the `helpers.regex()` function. A regular expression is provided as the only paramater to `helpers.regex()`. Read more about [regex-based validators](https://vuelidate-next.netlify.app/custom_validators.html#regex-based-validator).

```ts
const phoneNumberValidator = helpers.regex(
  /^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$/
);
```

To validate your form with `Vuelidate` and the composition API, you must define the validation `rules`. From Line 194, a computed property is used to return an object containing definitions matching the nesting of our field definitions from Line 79. For example, the validation rule for the `firstName` field is:

```ts
      firstName: {
        value: {
          required: helpers.withMessage("First Name is required.", required),
        },
      },
```

The above matches the nesting of the field definition for `firstName`:

```ts
      firstName: {
        label: "First Name",
        required: true,
        value: "",
        icon: "person",
        autocomplete: "given-name",
      },
```

We aren't validating the `firstName` property, rather, we are validating the `value` property inside `firstName` because the `value` property stores the value of the `firstName` field.

If we add a simpler field definition such as:

```ts
const form = reactive({
   firstName: '',
   surname: '',
   ...
})

```

Then, the validation rules could look like this:

```ts
const rules = computed(() => ({
      firstName: {
        required: helpers.withMessage("First Name is required.", required),
      },
   })
)
```

As you have noticed, to validate a field, you need to assign each validator as properties of the validation object assigned to the field. The above could be simplified as:

```ts
const rules = computed(() => ({
      firstName: {
        required,
      },
   })
)
```

In this case, we aren't defining any custom error message for the `required` validator. Each built-in validator imported from the `"@vuelidate/validators"` package has default messages. For the `required` validator it will be: `"Value is required"`. It is too generic for must use cases and, as a tutorial, I wanted to demonstrate how to assign custom error messages to built-in validators. `helpers.withMessage()` function call is used to define the custom message. It takes the error message as the first value and the built-in validator as the second value. There is another overload for the function. Read more [here](https://vuelidate-next.netlify.app/custom_validators.html#custom-error-messages).

On Line 254, the `useVuelidate` composable is used to return the validation object (a Vuelidate instance) which will be returned from the `setup` function and made available in our `template` section for error display. The `useVuelidate` function takes, at least, two parameters:

1. The `rules`,
2. The `form` schema,
3. A `validationConfig` object. Details about the `validationConfig` object [here](https://vuelidate-next.netlify.app/api/configuration.html). More about providing [global config to your Vuelidate instance](https://vuelidate-next.netlify.app/advanced_usage.html#providing-global-config-to-your-vuelidate-instance).

#### More on the `$autoDirty` property of `validationConfig`

The `$autoDirty` property is used to automatically track changes to the v-model of each validated field in your form. By default, `Vuelidate` doesn't track the changes and you are required to call the `$touch()` function for each field after you edit the field. Calling `$touch()` tells Vuelidate that that field has changed and that it should set the `$dirty` flag for the field to `true`. Without the `$dirty` flag being `true`, the field won't be marked as having an error, i.e., the `$error` flag for the field will be `false`, even though the `$invalid` flag is `true`. For example, you will be required to call `v$.firstName.value.$touch()` on `blur` event when you edit the `firstName` field.

Alternatively, you could use the `$model` proxy object as the `v-model` for the field. The `$model` proxy is proxy copy of the validated value. So, `v$.firstName.value.$model` is identical to `form.firstName.value`. When you use `$model` as the `v-model` value for each validated field, `Vuelidate` will automatically track the changes for those field and you don't have to call `$touch()` when you make changes.  For example, instead of `v-model="form[key].value"`, you could use `v-model="v$[key].value.$model"` to avoid calling `v$.firstName.value.$touch()`.

The `$autoDirty` property saves you all the stress by automatically track changes and marking each validated field as being `dirty` when you change the field. It also ensures that you do not have to use the `$model` proxy object. This is the strategy used for this tutorial.

Read more about the `$dirty` state [here from the docs](https://vuelidate-next.netlify.app/guide.html#the-dirty-state).

#### More on the `$lazy` property of `validationConfig`

By default `validationConfig.$lazy` is set to `false`. The effect of this property can make a big difference and show your deep understanding of how to validate forms and make them performant and user-friendly. Since `$lazy` is set to `false` by default, when a validated form is rendered, the `$dirty` flag for reach validated field will be automatically set to `true`. As discussed above, it results in the `$error` flag being `true` and error messages will be thrown for each errored field. So a user will immediately see validation errors and the field will be error state (showing red) even when they have not interacted with the form yet. This behaviour is not the best for user experience and could slow down the rendering of a large forms (with many fields).

However, when `$lazy` is `true`, errors will be thrown only when the field is set to `$dirty`. That is, errors will only be shown when the user has interacted with each field and while the value of each field has not passed the validation contraints. This is a better user experience and performant for large forms.

**As a bonus tip:** When you are validating a field which will require a lot of user inputs such as a `textarea` input or when you have an async validator (which could be fetching validation results from the database), you should be careful with the use of the global `$autoDirty` and `$lazy` properties. For the `textarea` field, you can override the config an set `$autoDirty` to `false` and `$lazy` to `true`. For example, in user registration form:

```ts
const isUnique = async function(value) {// Check if email `value` exist on database}
....
      email: {
        required,
        email,
        isUnique: helpers.withMessage("This email is already taken.", isUnique),
        $lazy: true,
        $autoDirty: false
      },
....
```

The above rule will ensure that the `isUnique` async function is only called when you explicitly call `v$.email.touch()` else, each key stroke will lead to a call to the `isUnique` function. Additionally, you should set up a `debounced` function to handle `isUnique` function to reduce the number of calls made due to rapidly-emitted events.

So, on Line 254:

```ts
const v$ = useVuelidate(rules, form, { $lazy: true, $autoDirty: true });
```

We are have globally set both values to be `true` so we will have tracking of changes across all validated fields and the form won't be automatically validated when rendered.

#### The `submitForm` function

Before we are done with the `setup` section, let's look at the submitForm function from Line 256. When the `submitForm` function is called, the first thing we do is to call `v$.value.$touch()`. Why? It is a good practice to call the `$touch()` function on the root of the `Vuelidate` instance i.e. `v$` to make sure that all validated field on the form are set to `$dirty` before will proceed with the rest of the form submission. Remember that when `$lazy` is set to `true`, validated fields will only emit errors when they have been interacted with. This means, you could end up submitting a form with some uncaught validation errors if you do not call `v$.value.$touch()` on the root object.

Since we are using the composition API, the `useValidate` function returns a `Ref`. And the `Ref` must be read with `.value`, hence `v$.value.$touch()`

After setting the entire form to as `dirty`, we also check that there is no error with `v$.value.$error`. If there is an error, we call Quasar `Notify` plugin to alert the user of the error. It is important to note that all errors in the each validated field is collected in the `errors` property of the root object. So `v$.value.$errors` contains all validation errors throughout the form. Each validated field will have its own `$errors` property such as `v$.value.firstName.$errors`. The `$errors` property is an array of validation objects. Each validation object contains the following:

```json
// Example for a `required` error on the `firstName.value` property
// v$.firstName.value.$errors
[{
 $message: "First Name is required."
 $params: Object
 $pending: false
 $property: "value"
 $propertyPath: "firstName.value"
 $response: false
 $uid: "firstName.value-required"
 $validator: "required"
}]
```

> Open your Chrome DevTools, switch to Vue tab. On the top right, click `Select component in page` and click on the `First Name` field. Within the components tree, click on the `CreateContact` component. On the right-hand side of the Vue devtools, under `setup`, expand `v$ > firstName > value > $errors`. If the `$error` array is empty, go to the form, type something inside the `First Name` and delete everything. This will trigger the `required` error for that field. Switch back to the Vue devtool and the `$errors` property for `v$.firstName.value` will be populated with a value similar to the above.

Try out the form by clicking the `Submit` button with the form unfilled. Then scroll up to see the validation errors. Use the Vue devtool to inspect the errors and instructed above.

On Line 260, we length the length of the `$errors` array and use that to compose the message for the error notification. If there is no error, a success notification is shown to the user.

On Line 280, we return the `Vuelidate` instance, `$v`, from the `setup` function for consumption with the `template` section.

#### The `template` section: displaying the errors

Within the `template` section, we add two props to the `q-input` component to help in display the validation errors.

1. The `error` prop notifies the component of the existence of the error and puts the component into an error state (which triggers red outlines, fills, and texts). We read from the `v$[key].$error`(where `key` is the string index of field object e.g. `firstName`) flag to set the value of the prop.
2. The `error-message` prop sets the error string which will be displayed below the input field. We read from the `$errors` array for each validated field by mapping the `$message` property and calling the `Array.join()` method to convert the array to a string separated by a new line (`"\n"`).

Play around with form by entering values in the validated fields and see how the errors are displaying in the form.

Save all your files, commit and merge with the master branch.

```bash
git add .
git commit -m "feat(ui): complete validation for new contact form"
git push --set-upstream origin 04-validate-new-contact-creation-form
git checkout master
git merge master 04-validate-new-contact-creation-form
git push
```

In this next lesson, we will build out the table for displaying contacts on the `home` page.


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/z0xr9m2ljler2cwnpsw2ycwa/validating-the-contact-form-with-vuelidate-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js • 2026-06-07*
