How to validate a custom form component in Vue 3?

Liuba Kuibida
5 min readFeb 8, 2022
3 AM by Uran Duo

A very long introduction. Feel free to skip 🥺

Building a form seems to be the most challenging aspect of web development work for me. HTML form elements are tricky. They are difficult to style (everybody knows that), but when it comes to incorporating accessibility, they can drive you crazy. Or not. It depends on your professional skills and willingness to apply accessibility concepts in your projects. Just in case, front-end masters always care about web accessibility, so the earlier you start to pay attention to it, the better.

Once you have created and styled the form fields, the funniest part of the development process begins — form validation. We use forms to collect the information we need, and form validation provides submitted data to match the requirements. For instance, we may need to inform users that a field is required or that we need to get data in a specific format. Proper form validation allows users to fix invalid data and ensures a good user experience.

Certainly, front-end developers can build a validation system from scratch by themself, but they usually don’t. Instead, they use libraries, which perform cross-platform validation on any form you might create. VeeValidate is one of such libraries built for Vue.js. It is easy, flexible, and fast. Besides, it works with native HTML elements or your favorite UI library components. And finally, you can use Vue.js as a progressive enhancement or in a complex setup — VeeValidate works perfectly fine with both.

Before we jump into coding, let me show you the design of a simple contact form I found on Dribble and styled it with Bulma (my favorite CSS framework). Here is also a demo published on GitHub Pages.

There are four text fields and radio buttons. Let’s see the validation for the very basic one — the name field. I built my form with reusable components, as a Vue 3 Forms course teaches, so in the beginning, my markup looks like this:

<BaseInput
label="Name"
type="text"
/>

I’ll come back to this piece of code at the very end, but for now, note that you deal with a component with props for a label. Once you have installed VeeValidate, we can move to the script section. Keep in mind that there are two ways we can use vee-validate in Vue 3: through template-based components or the use of composition API. The first approach is easy to understand and work with vee-validate, but it requires us to use their pre-bundled input component. As I mentioned, I built my form with custom components, so I’d try composition API.

First, we should import the composition functions useField and useForm from vee-validate:

import { useForm, useField } from 'vee-validate'
export default {
...
}

Here is a short explanation of these functions from the VeeValidate guide:

useField: Creates a form field with its validation state, you will use this inside your custom input components.

useForm: Creates a vee-validate's form context and associates any fields created with useField inside the same component or its children with it automatically, which you will use to create custom form components and to manage your fields in general.

The reason why we need them both is that we deal with a relatively big form. And providing validations separately for each field can clutter our code. Instead, we can define the validation schema using the useForm function by passing a validationSchema option. But first, let’s create a couple of validation methods for our name field inside the setup method:

const required = value => {
const requiredMessage = 'This field is required'
if (value === undefined || value === null) return requiredMessage
if (!String(value).length) return requiredMessage

return true
}
const minLength = (number, value) => {
if (String(value).length < number) return 'Please type at least ' + number + ' characters'

return true
}

Now we can check:

  1. If the name field isn’t empty.
  2. If a user has put the minimum amount of symbols.

Running ahead, I will reuse those methods for other fields like email or message. Usually, developers use pre-made methods from a validation library like yup, but I won’t touch it in this tutorial. Now it’s time to use our validationSchema — an object containing field names as keys and validation methods as the value for those keys.

const validationSchema = {
userName: (value) => {
const req = required(value)
if (req !== true) return req
const min = minLength(2, value)
if (min !== true) return min

return true
}
}

First, we run our validations against the current value and capture them in variables. They should return true (if they are valid) or a string (if not). If any of the two isn’t successful, we return its error message. If both validations pass, we return true to tell vee-validate that the field is valid.

The next step is to tell vee-validate about our validation schema using useForm:

useForm({
validationSchema
})

Finally, let’s create a useField call for our name field in the setup and return its value and error message from the setupmethod:

const { value: userName, errorMessage: userNameError } = useField('userName')return {
userName,
userNameError
}

To make our code clearer, we can extract the errors object from the useForm composition function:

const { errors } = useForm({
validationSchema
})

That allows us to shorten the code from the previous example:

const { value: userName } = useField('userName')return {
userName
}

Now we are ready to bind our field to the template.

<BaseInput
type="text"
label="Name"
v-model="userName"
:error="errors.userName"
/>

The job is done 🥳 If you want to see validation methods for other fields or wonder how to submit the form, please, check out the whole code here. Don’t hesitate to reach me in the comments if you have any questions or remarks. Thanks for reading and good luck with your vuetiful projects!

--

--

Liuba Kuibida

Master of cultural studies. Self-taught programmer. Stand with Ukraine 🇺🇦