vee-validate vs vuelidate
Form Validation Strategies in Vue.js Applications
vee-validatevuelidateSimilar Packages:

Form Validation Strategies in Vue.js Applications

vee-validate and vuelidate are two of the most prominent validation libraries for Vue.js, but they take fundamentally different approaches to solving form state and validation logic. vee-validate (v4+) is built specifically for Vue 3, offering both a component-based API and a composition API that tightly integrates with Vue's reactivity system to handle complex form scenarios, error collection, and submission flows. vuelidate started as a lightweight, model-driven validator for Vue 2, and its Vue 3 successor (@vuelidate/core) maintains a focus on simplicity and external state management, allowing validation rules to exist outside of the component template. While both aim to reduce boilerplate, vee-validate leans towards an all-in-one form solution, whereas vuelidate acts more as a validation state layer that you wire up manually.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
vee-validate011,262511 kB169a year agoMIT
vuelidate06,887104 kB2155 years agoMIT

Form Validation Strategies in Vue.js: vee-validate vs vuelidate

Choosing the right validation library can make or break the developer experience in a Vue application. Both vee-validate and vuelidate solve the same problem — ensuring user input meets specific criteria — but they differ significantly in architecture, API design, and scope. Let's break down how they handle real-world engineering challenges.

🏗️ Architecture: Components vs. Composition

vee-validate offers a dual approach. You can use dedicated components like <Form> and <Field> that abstract away the wiring, or use the useForm and useField composables for full control. This makes it flexible for both rapid prototyping and complex custom inputs.

// vee-validate: Component-based approach
<template>
  <Form @submit="onSubmit">
    <Field name="email" rules="required|email" />
    <ErrorMessage name="email" />
    <button type="submit">Submit</button>
  </Form>
</template>

vuelidate is purely logic-based. It provides a useVuelidate composable that returns a reactive validation state object. You are responsible for binding the $model, $error, and $touch properties to your template inputs manually.

// vuelidate: Composition API approach
<script setup>
import { useVuelidate } from '@vuelidate/core'
import { required, email } from '@vuelidate/validators'

const state = reactive({ email: '' })
const rules = { email: { required, email } }
const v$ = useVuelidate(rules, state)
</script>

<template>
  <input v-model="state.email" @blur="v$.email.$touch" />
  <span v-if="v$.email.$error">Invalid email</span>
</template>

📝 Defining Validation Rules

vee-validate uses a string-based syntax for common rules (like 'required|email') or an object for more complex logic. It comes with a built-in set of common rules, but you can easily register global custom rules.

// vee-validate: String or Object rules
<Field name="password" rules="required|min:6" />

// Or with object for params
<Field name="age" :rules="{ required: true, min_value: 18 }" />

vuelidate requires you to import validators from a separate package (@vuelidate/validators) and construct a rules object that mirrors your state structure. This keeps validation logic explicit but adds more imports.

// vuelidate: Imported validators
import { required, minLength } from '@vuelidate/validators'

const rules = {
  password: { required, minLength: minLength(6) }
}

🚨 Error Handling and Messages

vee-validate has built-in components for displaying errors, such as <ErrorMessage>. It automatically tracks which field is touched and displays messages only when relevant, reducing boilerplate in your templates.

// vee-validate: Built-in error component
<Field name="username" rules="required" />
<ErrorMessage name="username" class="error-text" />

vuelidate does not provide UI components. You must write your own logic to check $error and $dirty flags to decide when to show messages. This gives you full control but requires more repetitive code.

// vuelidate: Manual error rendering
<input v-model="state.username" @blur="v$.username.$touch" />
<span v-if="v$.username.$dirty && v$.username.$error" class="error-text">
  Username is required
</span>

📬 Form Submission and State

vee-validate manages the entire form lifecycle. The <Form> component handles the submit event, prevents submission if invalid, and aggregates all field values into a single object passed to your handler.

// vee-validate: Handled submission
<Form @submit="handleSubmit">
  <Field name="email" />
  <button type="submit">Send</button>
</Form>

<script setup>
const handleSubmit = (values) => {
  console.log(values.email) // Access all values here
}
</script>

vuelidate only validates state. You must handle the form submit event yourself, call $validate() to trigger checks, and manually check $invalid before proceeding with your API call.

// vuelidate: Manual submission check
<script setup>
const submitForm = async () => {
  const isCorrect = await v$.value.$validate()
  if (!isCorrect) return
  
  await api.submit(state)
}
</script>

🌐 Async Validation

vee-validate supports async rules out of the box. You can define a rule that returns a promise, and the field will handle the pending state automatically.

// vee-validate: Async rule
const checkUnique = async (value) => {
  const { data } = await api.checkUsername(value)
  return data.available || 'Username taken'
}

<Field name="username" :rules="checkUnique" />

vuelidate also supports async validators, but you need to wrap them correctly and manage the $pending flag in your UI manually to show loading spinners.

// vuelidate: Async validator
const unique = (value) => {
  return api.checkUsername(value).then(res => res.available)
}

const rules = { username: { unique } }

// In template
<span v-if="v$.username.$pending">Checking...</span>

🤝 Similarities: Shared Ground

Despite their differences, both libraries share core goals and capabilities.

1. ⚛️ Vue Reactivity Integration

  • Both leverage Vue's reactivity system to update validation states instantly.
  • Support for Vue 3 Composition API is standard in their latest versions.
// Both use reactive state
const state = reactive({ field: '' }) // Works in both ecosystems

2. 🔌 Custom Validator Support

  • You can write your own validation logic in plain JavaScript.
  • Both allow combining multiple rules together.
// vee-validate custom rule
defineRule('odd', (value) => value % 2 !== 0)

// vuelidate custom rule
const odd = (value) => value % 2 !== 0

3. 📱 Cross-Field Validation

  • Both can validate fields based on the value of other fields (e.g., password confirmation).
// vee-validate: referencing other fields
<Field name="confirm" rules="confirmed:password" />

// vuelidate: accessing state in rule
const sameAs = (other) => (value) => value === state[other]

📊 Summary: Key Differences

Featurevee-validatevuelidate
Primary APIComponents (<Field>) + ComposablesComposables (useVuelidate) only
Error UIBuilt-in <ErrorMessage> componentManual implementation required
Form SubmissionHandled by <Form> componentManual $validate() call
Rule SyntaxStrings ('required') or ObjectsImported Validator Functions
Vue 3 SupportNative (v4+)Via @vuelidate/core
MaintenanceActive and frequent updatesSlower pace, maintenance mode for Vue 2

💡 The Big Picture

vee-validate is like a full-service form manager 🧰. It handles the validation, the error display, and the submission logic. It is ideal for dashboards, settings pages, and any form where you want to minimize boilerplate and ensure consistency across your UI.

vuelidate is like a precision validation tool 🔍. It validates your data model and stays out of your UI layer. It is suitable for simpler forms or teams that prefer to build their own form components from scratch without opinionated wrappers.

Final Thought: For most modern Vue 3 applications, vee-validate offers a more complete and sustainable solution. However, if you need lightweight validation on a data model without form UI constraints, vuelidate remains a viable option — provided you are using the Vue 3 compatible package.

How to Choose: vee-validate vs vuelidate

  • vee-validate:

    Choose vee-validate if you are building complex forms in Vue 3 that require robust error handling, custom input components, or server-side error integration. It is the better fit for projects that need a complete form management solution, including submission handling, dirty state tracking, and built-in support for UI frameworks like Vuetify or Tailwind. Its active maintenance and Vue 3-first architecture make it a safer long-term bet for new enterprise applications.

  • vuelidate:

    Choose vuelidate (specifically @vuelidate/core for Vue 3) if you prefer a lightweight, model-centric approach where validation logic is decoupled from your UI components. It is suitable for simpler forms or legacy Vue 2 projects where you want minimal overhead and do not need advanced form submission features. However, be aware that development velocity has slowed compared to vee-validate, so evaluate if the community support meets your project's risk tolerance.

README for vee-validate

Painless Vue forms



Features

  • 🍞 Easy: Declarative validation that is familiar and easy to setup
  • 🧘‍♀️ Flexible: Synchronous, Asynchronous, field-level or form-level validation
  • ⚡️ Fast: Build faster forms faster with intuitive API and small footprint
  • 🏏 Minimal: Only handles the complicated form concerns, gives you full control over everything else
  • 😎 UI Agnostic: Works with native HTML elements or your favorite UI library components
  • 🦾 Progressive: Works whether you use Vue.js as a progressive enhancement or in a complex setup
  • ✅ Built-in Rules: Companion lib with 25+ Rules that covers most needs in most web applications
  • 🌐 i18n: 45+ locales for built-in rules contributed by developers from all over the world

Getting Started

Installation

# Install with yarn
yarn add vee-validate

# Install with npm
npm install vee-validate --save

Vue version support

The main v4 version supports Vue 3.x only, for previous versions of Vue, check the following the table

vue Versionvee-validate versionDocumentation Link
2.x2.x or 3.xv2 or v3
3.x4.xv4

Usage

vee-validate offers two styles to integrate form validation into your Vue.js apps.

Composition API

The fastest way to create a form and manage its validation, behavior, and values is with the composition API.

Create your form with useForm and then use defineField to create your field model and props/attributes and handleSubmit to use the values and send them to an API.

<script setup>
import { useForm } from 'vee-validate';

// Validation, or use `yup` or `zod`
function required(value) {
  return value ? true : 'This field is required';
}

// Create the form
const { defineField, handleSubmit, errors } = useForm({
  validationSchema: {
    field: required,
  },
});

// Define fields
const [field, fieldProps] = defineField('field');

// Submit handler
const onSubmit = handleSubmit(values => {
  // Submit to API
  console.log(values);
});
</script>

<template>
  <form @submit="onSubmit">
    <input v-model="field" v-bind="fieldProps" />
    <span>{{ errors.field }}</span>

    <button>Submit</button>
  </form>
</template>

You can do so much more than this, for more info check the composition API documentation.

Declarative Components

Higher-order components can also be used to build forms. Register the Field and Form components and create a simple required validator:

<script setup>
import { Field, Form } from 'vee-validate';

// Validation, or use `yup` or `zod`
function required(value) {
  return value ? true : 'This field is required';
}

// Submit handler
function onSubmit(values) {
  // Submit to API
  console.log(values);
}
</script>

<template>
  <Form v-slot="{ errors }" @submit="onSubmit">
    <Field name="field" :rules="required" />

    <span>{{ errors.field }}</span>

    <button>Submit</button>
  </Form>
</template>

The Field component renders an input of type text by default but you can control that

📚 Documentation

Read the documentation and demos.

Contributing

You are welcome to contribute to this project, but before you do, please make sure you read the contribution guide.

Credits

Emeriti

Here we honor past contributors and sponsors who have been a major part on this project.

⚖️ License

Released under MIT by @logaretm.