Agent skill
web-forms-vee-validate
VeeValidate v4 patterns - useForm, useField, defineField, useFieldArray, schema validation with Composition API
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/web-forms-vee-validate
SKILL.md
VeeValidate Form Validation Patterns
Quick Guide: Use VeeValidate v4 for Vue 3 form validation with Composition API. Use
useFormfor form state,defineFieldfor quick field setup,useFieldfor custom input components, anduseFieldArrayfor dynamic lists. Always wrap schema libraries withtoTypedSchema(). Always usefield.key(not index) as iteration key in field arrays.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use toTypedSchema() wrapper when using schema libraries in v4 - raw schemas won't work)
(You MUST use field.key as iteration key in useFieldArray - NEVER use array index)
(You MUST use function form () => props.name or toRef() in useField for prop reactivity)
(You MUST initialize field array values in initialValues - undefined arrays cause errors)
</critical_requirements>
Auto-detection: VeeValidate, vee-validate, useForm, useField, defineField, useFieldArray, toTypedSchema, ErrorMessage, Form component
When to use:
- Building Vue 3 forms with validation requirements
- Managing complex form state with multiple fields
- Creating dynamic forms with add/remove field capabilities
- Integrating schema validation libraries with
toTypedSchema() - Building multi-step wizard forms
When NOT to use:
- Single input without validation (use native v-model)
- Server-only forms with server actions (use native form submission)
- Read-only data display (not a form scenario)
Detailed Resources:
- examples/core.md - defineField, useField, form meta, eager validation
- examples/validation.md - Zod/Yup/Valibot schema integration, conditional validation
- examples/arrays.md - useFieldArray, nested arrays, reordering
- reference.md - Decision frameworks, API reference tables, anti-patterns
Philosophy
VeeValidate v4 embraces Vue 3's Composition API as the primary approach, enabling seamless integration with any UI library. Validation logic is decoupled from presentation, allowing schema-first validation with full TypeScript inference.
Core Principles:
- Composition API first - Use
useForm,useField,defineFieldfor seamless Vue 3 integration - Schema-first validation - Prefer declarative schemas over inline rules
- Full type safety - TypeScript inference from schemas and generics
- UI agnostic - Works with any component library or native inputs
- Minimal re-renders - Efficient reactivity through Vue's reactive system
defineField vs useField:
| Feature | defineField |
useField |
|---|---|---|
| Use case | Quick form setup with native inputs | Building reusable custom input components |
| Form context | Always requires form context | Optional form integration |
| Best for | Application-level forms | Component library development |
Core Patterns
Pattern 1: Basic Form with defineField
Use useForm with defineField for the fastest form setup. defineField returns a [model, attrs] tuple for v-model binding. See examples/core.md for full examples.
<script setup lang="ts">
import { useForm } from "vee-validate";
import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod";
const schema = toTypedSchema(
z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "At least 8 characters"),
}),
);
const { handleSubmit, errors, defineField } = useForm({
validationSchema: schema,
});
const [email, emailAttrs] = defineField("email");
const onSubmit = handleSubmit((values) => {
// values is fully typed from schema
});
</script>
Pattern 2: Custom Input Components with useField
Use useField when building reusable input components. Critical: use function form () => props.name to maintain reactivity. See examples/core.md for full component example.
<script setup lang="ts">
import { useField } from "vee-validate";
const props = defineProps<{ name: string }>();
// CRITICAL: Function form maintains reactivity
const { value, errorMessage, handleBlur, meta } = useField<string>(
() => props.name,
undefined,
{ validateOnValueUpdate: false },
);
</script>
Pattern 3: Schema Validation with toTypedSchema
Always wrap schema libraries with toTypedSchema(). Initialize ALL fields used in refine/superRefine - Zod skips refinements when keys are undefined. See examples/validation.md for Zod, Yup, and Valibot examples.
import { toTypedSchema } from "@vee-validate/zod";
// CORRECT: Wrapped schema
const schema = toTypedSchema(z.object({ email: z.string().email() }));
// WRONG: Raw schema won't work with VeeValidate
const schema = z.object({ email: z.string().email() });
Pattern 4: Dynamic Arrays with useFieldArray
Use useFieldArray for add/remove/reorder patterns. Always use field.key as :key, never array index. Initialize arrays in initialValues. See examples/arrays.md for full patterns.
<script setup lang="ts">
import { useForm, useFieldArray } from "vee-validate";
const { handleSubmit } = useForm({
initialValues: { users: [{ name: "", email: "" }] },
});
const { fields, push, remove } = useFieldArray("users");
</script>
<template>
<!-- CORRECT: field.key as key -->
<div v-for="(field, index) in fields" :key="field.key">
<input v-model="field.value.name" />
</div>
</template>
Pattern 5: Server-Side Error Handling
Set errors from API responses using setErrors (multiple) or setFieldError (single).
const { handleSubmit, setErrors, setFieldError } = useForm({ ... });
const onSubmit = handleSubmit(async (values) => {
try {
await api.createUser(values);
} catch (error) {
if (error.response?.data?.errors) {
// Set multiple field errors from API
setErrors(mapApiErrors(error.response.data.errors));
} else {
setFieldError("apiError", "Something went wrong");
}
}
});
Pattern 6: Form Meta and State
Access aggregated form state for UX features like dirty tracking, submit button state, and reset. See examples/core.md for full example.
<script setup lang="ts">
const { handleSubmit, meta, isSubmitting, resetForm } = useForm({ ... });
</script>
<template>
<button :disabled="!meta.valid || !meta.dirty || isSubmitting">
{{ isSubmitting ? "Saving..." : "Save" }}
</button>
<p v-if="meta.dirty">You have unsaved changes</p>
</template>
<red_flags>
RED FLAGS
High Priority Issues:
- Missing
toTypedSchema()wrapper - raw schemas silently fail to validate - Using array index as
:keyinuseFieldArray- causes form state corruption on add/remove - Direct
props.nameinuseField- loses reactivity when prop changes - Undefined
initialValuesfor field arrays - causes runtime errors
Medium Priority Issues:
validateOnValueUpdateenabled everywhere - validates on every keystroke (noisy UX)- Not handling async validation errors - API failures need
setErrors()orsetFieldError() - Forgetting
resetForm()after submission - form stays dirty after success - Multiple
useFormcalls in same component - creates conflicting form contexts - Not using
meta.touchedfor error display - shows errors before user interaction
Gotchas & Edge Cases:
errorshas first error per field;errorBaghas ALL errors per field as arraysmeta.validmay be false during initial render before validation runs- Nested fields use dot notation:
defineField('user.profile.name') - Array field errors use bracket notation:
errors['items[0].name'] resetForm({ values: data })notresetForm(data)- wrong structure silently failskeepValuesOnUnmount: false(default) drops values of unmounted fields - settruefor multi-step forms- Mixing
<Form>component withuseForm()creates conflicting contexts - pick one approach - Zod
refine/superRefinedo NOT execute when object keys are missing - always initialize all fields
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST use toTypedSchema() wrapper when using schema libraries in v4 - raw schemas won't work)
(You MUST use field.key as iteration key in useFieldArray - NEVER use array index)
(You MUST use function form () => props.name or toRef() in useField for prop reactivity)
(You MUST initialize field array values in initialValues - undefined arrays cause errors)
Failure to follow these rules will break form validation, cause reactivity issues, and corrupt form state.
</critical_reminders>
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?