Harnessing VeeValidate’s useField for Modular Vue 3 Forms
Learn how VeeValidate’s useField composable lets you build reusable, scoped validation logic in Vue 3. A hands‑on example shows reactive fields, async rules, and common pitfalls.
23 Dec 2025, 00:58 UTC

Why Re‑usable Validation Matters
When a Vue 3 app grows, form logic tends to become duplicated. A single form component that pulls in a lot of validation code can become a maintenance nightmare. VeeValidate’s useField composable lets you encapsulate field state in a tiny, composable block that can be dropped into any component.
The Core Problem
Typical form libraries expose a single Form component that manages all fields. That works for small forms, but as soon as you split a form into reusable sub‑components, you risk sharing state or writing the same validation rules over and over.
Thesis: useField Gives You Scoped, Reactive Field Logic
VeeValidate’s useField returns a set of reactive refs that are isolated to the instance that called it. This means you can create a <TextInput> component that internally calls useField and expose only the value and errorMessage props to the parent. The parent form stays clean, and each field keeps its own validation state.
Key Features of useField
- Reactive refs:
value,errorMessage,isValidatingarerefobjects that update automatically. - Built‑in rule integration: Pass a string of pipe‑separated rules or a custom function via the
rulesoption. - Async validation support: Return a Promise from a rule and VeeValidate will set
isValidatingduring the pending state. - Isolation: Multiple
useFieldcalls in the same form never clash.
Concrete Example: A Re‑usable Email Field
Below is a minimal component that uses useField to create a reusable email input. The parent form can drop it in without knowing the validation logic.
// EmailInput.vue
<div>
<label :for="name">Email</label>
<input
:id="name"
v-model="value"
@blur="handleBlur"
@input="handleChange"
type="email"
/>
<p v-if="errorMessage" class="error">{{ errorMessage }}</p>
</div>
Usage in a parent form:
// MyForm.vue
<template>
<form @submit.prevent="submit">
<EmailInput name="userEmail" />
<button type="submit" :disabled="isSubmitting || isValidating">Submit</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
import { useForm } from 'vee-validate';
import EmailInput from './EmailInput.vue';
const { handleSubmit, isSubmitting, isValidating } = useForm();
const submit = handleSubmit(values => {
console.log('Form values:', values);
});
</script>
In this setup:
- The
EmailInputcomponent declares its ownuseFieldinstance. - Each instance receives a unique
nameprop, ensuring isolated state. - The parent form only cares about the final
valuesobject.
Common Pitfalls and How to Avoid Them
1. Using useField Inside a v-for Without Unique Names
When rendering a dynamic list of fields, every iteration must supply a distinct name. If two fields share a name, their value and errorMessage refs collide.
// BAD – all fields share the same name
<div v-for="field in fields">
<EmailInput name="userEmail" />
</div>
Instead, use the index or a unique identifier:
<div v-for="(field, i) in fields" :key="field.id">
<EmailInput :name="`userEmail-${i}`" />
</div>
2. Nested Object Fields Don’t Reset Automatically
If a field’s value is a deep object (e.g., { address: { city: '' } }), resetForm won’t clear nested properties. You must manually clear them or use resetField on each nested field.
Trade‑off: Over‑Abstraction vs. Simplicity
Encapsulating validation in useField components gives you clean, reusable code but adds an extra layer of indirection. For very simple forms, a single Form component with inline rules may be easier to read. Use useField when you anticipate re‑use across multiple forms or when you need to isolate complex validation logic.
Actionable Checklist
- Wrap each field in a component that calls
useFieldwith a uniquename. - Expose only
valueanderrorMessageto the parent. - When rendering lists, generate unique names per iteration.
- For nested objects, call
resetFieldmanually or reset the entire form. - Leverage async rules by returning a Promise; observe
isValidatingto provide user feedback.
By following these patterns, you keep your form logic modular, maintainable, and fully reactive across Vue 3 components.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.