VeeValidate — Full Form Library

Schemas, Fields, Errors — Out of the Box

VeeValidate — Full Form Library

VeeValidate handles real-world forms — schemas, async validation, field arrays, and scoped errors — without you writing the plumbing.

4 min read Level 3/5 #vue#forms#vee-validate
What you'll learn
  • Install vee-validate plus yup or zod
  • Use useForm with a validationSchema
  • Surface errors per field

VeeValidate is the de facto form library for Vue. It supports schema validation (Yup or Zod), async rules, and gives you a useForm composable that tracks every flag you care about.

Install

npm install vee-validate yup @vee-validate/yup

useForm With a Schema

<script setup lang="ts">
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/yup'
import * as yup from 'yup'

const schema = toTypedSchema(
  yup.object({
    email: yup.string().email().required(),
    password: yup.string().min(8).required(),
  })
)

const { defineField, handleSubmit, errors } = useForm({ validationSchema: schema })

const [email, emailAttrs] = defineField('email')
const [password, passwordAttrs] = defineField('password')

const onSubmit = handleSubmit(values => {
  console.log('submit', values)
})
</script>

<template>
  <form @submit="onSubmit">
    <input v-model="email" v-bind="emailAttrs" />
    <p v-if="errors.email">{{ errors.email }}</p>

    <input v-model="password" type="password" v-bind="passwordAttrs" />
    <p v-if="errors.password">{{ errors.password }}</p>

    <button type="submit">Sign in</button>
  </form>
</template>

The schema drives both validation and TypeScript types — values inside handleSubmit are fully typed.

Form Components

There is also a higher-level API using Form, Field, and ErrorMessage components:

<Form :validation-schema="schema" @submit="onSubmit">
  <Field name="email" type="email" />
  <ErrorMessage name="email" />

  <Field name="password" type="password" />
  <ErrorMessage name="password" />

  <button type="submit">Sign in</button>
</Form>

Async Validation

Rules can be async — useful for “is this username available” checks:

const schema = yup.object({
  username: yup.string().test('unique', 'Taken', async v => {
    if (!v) return true
    const res = await fetch(`/api/check?u=${v}`)
    return res.status === 200
  }),
})

VeeValidate debounces validation, tracks isValidating per field, and pauses submit while async rules run.

Zod + Vue →