Using Async Validation in VeeValidate for Server‑Side Checks
Learn how to add asynchronous validation rules in VeeValidate, see a working Vue 3 example, and understand the limits and pitfalls to avoid.
16 Jul 2026, 13:17 UTC

Quick answer: enable async validation
In VeeValidate you can run asynchronous checks (e.g., asking the server if a username is already taken) by defining a rule that returns a Promise and registering it with async: true. The validation provider will then wait for the promise to settle before showing an error.
How async validation works
VeeValidate treats any rule whose validator function returns a Promise as asynchronous only when the rule’s options include async: true. The promise must resolve to an object { valid: boolean, message?: string } (or a simple boolean). While the promise is pending, the field is considered valid; once it resolves, the valid value determines whether an error is shown.
// src/validation.js
import { extend } from '@vee-validate/core'
// Example: check username availability via an API endpoint
extend('username-available', {
validator: async (value) => {
try {
const response = await fetch(`/api/users/check?username=${encodeURIComponent(value)}`)
const data = await response.json() // { available: true }
return {
valid: data.available,
message: data.available ? '' : 'Username already taken'
}
} catch (err) {
// Network error – treat as invalid and show a generic message
return { valid: false, message: 'Unable to verify username' }
}
},
async: true // <-- tells VeeValidate this rule is asynchronous
})
Using the rule in a Vue 3 template
Wrap the input with ValidationProvider, add the rule to the rules prop, and optionally adjust the debounce time.
<template>
<ValidationProvider
rules="username-available|required"
v-slot="{ errors }"
:debounce="400">
<label>Username</label>
<input v-model="username" />
<span class="error">{{ errors[0] }}</span>
</ValidationProvider>
</template>
<script setup>
import { ref } from 'vue'
import { ValidationProvider } from '@vee-validate/vue3'
import '../validation' // registers the rule
const username = ref('')
</script>
Limits and common mistakes
- Missing
async: true– If you forget this flag, VeeValidate treats the promise‑returning validator as a synchronous rule. The promise is ignored, the field may appear valid instantly, and stale errors can persist. - Incorrect return shape – The resolved value must be an object with a
validboolean (or a plain boolean). Returning just a string orundefinedcauses VeeValidate to treat the result asfalsewithout a message, leading to confusing UI. - Debounce trade‑offs – By default, VeeValidate debounces async validations for 300 ms to limit request frequency. Setting
:debounce="0"removes the delay and can flood the server with rapid requests; setting it too high (e.g., 1000 ms) makes the UI feel sluggish. Adjust the value based on your API’s rate limits and user‑experience goals. - Unhandled promise rejections – If the validator throws or the fetch promise rejects, the validation fails. It’s good practice to catch errors inside the validator and return a meaningful
{ valid: false, message: '…' }instead of letting the rejection propagate.
Verification steps (for your own testing)
- Create a minimal Vue 3 project (
npm create vue@latest) and install@vee-validate/coreand@vee-validate/vue3. - Add the async rule definition shown above in a file imported once (e.g.,
main.js). - Use the
ValidationProvidertemplate snippet with an input bound to aref. - Open the browser devtools Network tab, type a username, and observe that a request to
/api/users/checkis sent only after the debounce delay. - Change the
:debounceprop to 0 and 500 ms and confirm that the request frequency changes accordingly. - Toggle the
async: trueflag to see the validation behave synchronously (errors appear immediately, ignoring the promise).
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.