0
# Form Management
1
2
Form-level composables for managing complete form state, validation, submission handling, and providing context to child components. These composables are the foundation for building complex forms with VeeValidate.
3
4
## Capabilities
5
6
### useForm Composable
7
8
Creates a form context with comprehensive validation management and state tracking.
9
10
```typescript { .api }
11
/**
12
* Creates a form context with validation management
13
* @param options - Optional form configuration
14
* @returns Form context object with state and methods
15
*/
16
function useForm<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues>(
17
options?: FormOptions<TValues>
18
): FormContext<TValues, TOutput>;
19
20
interface FormOptions<TValues extends GenericObject> {
21
validationSchema?: MaybeRef<RawFormSchema<TValues> | TypedSchema<TValues, TOutput> | YupSchema<TValues> | undefined>;
22
initialValues?: PartialDeep<TValues>;
23
initialErrors?: Partial<FlattenAndSetPathsType<TValues, string | string[] | undefined>>;
24
initialTouched?: Partial<FlattenAndSetPathsType<TValues, boolean>>;
25
validateOnMount?: boolean;
26
keepValuesOnUnmount?: MaybeRef<boolean>;
27
name?: string;
28
}
29
30
interface FormContext<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues> {
31
// Form state
32
values: TValues;
33
errors: ComputedRef<FormErrors<TValues>>;
34
meta: ComputedRef<FormMeta<TValues>>;
35
isSubmitting: Ref<boolean>;
36
isValidating: Ref<boolean>;
37
submitCount: Ref<number>;
38
controlledValues: Ref<TValues>;
39
40
// Form methods
41
handleSubmit: HandleSubmitFactory<TValues, TOutput> & { withControlled: HandleSubmitFactory<TValues, TOutput> };
42
validate(opts?: Partial<ValidationOptions>): Promise<FormValidationResult<TValues, TOutput>>;
43
validateField<TPath extends Path<TValues>>(field: TPath, opts?: Partial<ValidationOptions>): Promise<ValidationResult<TOutput[TPath]>>;
44
45
// State mutations
46
setFieldValue<T extends Path<TValues>>(field: T, value: PathValue<TValues, T>, shouldValidate?: boolean): void;
47
setFieldError(field: Path<TValues>, message: string | string[] | undefined): void;
48
setErrors(fields: Partial<FlattenAndSetPathsType<TValues, string | string[] | undefined>>): void;
49
setValues(fields: PartialDeep<TValues>, shouldValidate?: boolean): void;
50
setFieldTouched(field: Path<TValues>, isTouched: boolean): void;
51
setTouched(fields: Partial<Record<Path<TValues>, boolean>> | boolean): void;
52
resetForm(state?: Partial<FormState<TValues>>, opts?: Partial<ResetFormOpts>): void;
53
resetField(field: Path<TValues>, state?: Partial<FieldState>): void;
54
55
// Form context methods
56
handleReset(): void;
57
submitForm(e?: unknown): Promise<void>;
58
createPathState<TPath extends Path<TValues>>(path: MaybeRef<TPath>, config?: Partial<PathStateConfig<TOutput[TPath]>>): PathState<PathValue<TValues, TPath>>;
59
defineField<TPath extends Path<TValues>, TValue = PathValue<TValues, TPath>, TExtras extends GenericObject = GenericObject>(
60
path: MaybeRefOrGetter<TPath>,
61
config?: Partial<InputBindsConfig<TValue, TExtras>> | LazyInputBindsConfig<TValue, TExtras>
62
): [Ref<TValue>, Ref<BaseFieldProps & TExtras>];
63
}
64
65
type HandleSubmitFactory<TValues extends GenericObject, TOutput extends GenericObject = TValues> = <TReturn = unknown>(
66
cb: SubmissionHandler<TValues, TOutput, TReturn>,
67
onSubmitValidationErrorCb?: InvalidSubmissionHandler<TValues, TOutput>
68
) => (e?: Event) => Promise<TReturn | undefined>;
69
```
70
71
**Usage Examples:**
72
73
```typescript
74
import { useForm } from "vee-validate";
75
import * as yup from "yup";
76
77
// Basic form setup
78
const { handleSubmit, errors, meta, values } = useForm();
79
80
// Form with validation schema
81
const schema = yup.object({
82
name: yup.string().required("Name is required"),
83
email: yup.string().email("Email is invalid").required("Email is required"),
84
age: yup.number().min(18, "Must be at least 18").required("Age is required")
85
});
86
87
const {
88
handleSubmit,
89
errors,
90
meta,
91
values,
92
setFieldValue,
93
resetForm
94
} = useForm({
95
validationSchema: schema,
96
initialValues: {
97
name: "",
98
email: "",
99
age: 0
100
}
101
});
102
103
// Handle form submission
104
const onSubmit = handleSubmit((values) => {
105
console.log("Form submitted:", values);
106
// Submit to API
107
}, (ctx) => {
108
console.log("Form validation failed:", ctx.errors);
109
});
110
111
// Programmatic field manipulation
112
const updateUser = () => {
113
setFieldValue("name", "John Doe");
114
setFieldValue("email", "john@example.com");
115
};
116
117
// Reset form to initial state
118
const handleReset = () => {
119
resetForm();
120
};
121
122
// Advanced form with custom validation
123
const {
124
handleSubmit,
125
validate,
126
validateField,
127
setErrors
128
} = useForm({
129
validationSchema: {
130
username: (value) => {
131
if (!value) return "Username is required";
132
if (value.length < 3) return "Username too short";
133
return true;
134
},
135
password: (value) => {
136
if (!value) return "Password is required";
137
if (value.length < 8) return "Password must be at least 8 characters";
138
return true;
139
},
140
confirmPassword: (value, { form }) => {
141
if (!value) return "Please confirm password";
142
if (value !== form.password) return "Passwords do not match";
143
return true;
144
}
145
}
146
});
147
148
// Manual validation
149
const validateForm = async () => {
150
const result = await validate();
151
if (!result.valid) {
152
console.log("Form is invalid:", result.errors);
153
}
154
};
155
156
// Validate specific field
157
const validateUsername = async () => {
158
const result = await validateField("username");
159
if (!result.valid) {
160
console.log("Username is invalid:", result.errors);
161
}
162
};
163
```
164
165
### useFormContext Composable
166
167
Accesses the current form context via dependency injection from a parent Form component or useForm call.
168
169
```typescript { .api }
170
/**
171
* Accesses the current form context via dependency injection
172
* @returns Form context from nearest parent Form component or useForm call
173
* @throws Error if no form context is found
174
*/
175
function useFormContext<TValues extends GenericObject = GenericObject, TOutput extends GenericObject = TValues>(): FormContext<TValues, TOutput>;
176
```
177
178
**Usage Examples:**
179
180
```typescript
181
import { useFormContext } from "vee-validate";
182
183
// In a child component that needs access to form context
184
const ChildComponent = {
185
setup() {
186
const form = useFormContext();
187
188
// Access form state
189
const isFormValid = computed(() => form.meta.value.valid);
190
const formErrors = computed(() => form.errors.value);
191
192
// Use form methods
193
const clearForm = () => {
194
form.resetForm();
195
};
196
197
const submitForm = async () => {
198
const result = await form.validate();
199
if (result.valid) {
200
// Handle submission
201
}
202
};
203
204
return {
205
isFormValid,
206
formErrors,
207
clearForm,
208
submitForm
209
};
210
}
211
};
212
```
213
214
## Form State Types
215
216
### FormMeta Interface
217
218
Provides aggregate metadata about the form's validation state.
219
220
```typescript { .api }
221
interface FormMeta<TValues extends GenericObject> {
222
touched: boolean; // True if any field has been touched
223
dirty: boolean; // True if any field value differs from initial
224
valid: boolean; // True if all fields pass validation
225
pending: boolean; // True if any validation is in progress
226
initialValues?: Partial<TValues>; // Original form values
227
}
228
```
229
230
### FormState Interface
231
232
Complete form state structure for reset operations and state management.
233
234
```typescript { .api }
235
interface FormState<TValues> {
236
values: PartialDeep<TValues>; // Current field values
237
errors: Partial<Record<Path<TValues>, string | undefined>>; // Field error messages
238
touched: Partial<Record<Path<TValues>, boolean>>; // Field touched states
239
submitCount: number; // Number of submission attempts
240
}
241
```
242
243
### FormErrors Type
244
245
Type-safe error mapping for form fields.
246
247
```typescript { .api }
248
type FormErrors<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string | undefined>>;
249
type FormErrorBag<TValues extends GenericObject> = Partial<Record<Path<TValues> | '', string[]>>;
250
```
251
252
## Submission Handling
253
254
### SubmissionHandler Type
255
256
Type definition for form submission callbacks.
257
258
```typescript { .api }
259
type SubmissionHandler<TInput extends GenericObject = GenericObject, TOutput = TInput, TReturn = unknown> = (
260
values: TOutput,
261
ctx: SubmissionContext<TInput>
262
) => TReturn;
263
264
interface SubmissionContext<TInput extends GenericObject = GenericObject> extends FormActions<TInput> {
265
evt?: Event; // Original submit event
266
controlledValues: Partial<TInput>; // Current controlled form values
267
}
268
```
269
270
### InvalidSubmissionHandler Type
271
272
Type definition for handling invalid form submissions.
273
274
```typescript { .api }
275
type InvalidSubmissionHandler<TInput extends GenericObject = GenericObject, TOutput extends GenericObject = TInput> = (
276
ctx: InvalidSubmissionContext<TInput, TOutput>
277
) => void;
278
279
interface InvalidSubmissionContext<TInput extends GenericObject = GenericObject, TOutput extends GenericObject = TInput> {
280
values: TInput; // Current form values
281
evt?: Event; // Original submit event
282
errors: Partial<Record<Path<TInput>, string>>; // Current field errors
283
results: FormValidationResult<TInput, TOutput>['results']; // Detailed validation results
284
}
285
```
286
287
**Submission Examples:**
288
289
```typescript
290
import { useForm } from "vee-validate";
291
292
const { handleSubmit } = useForm();
293
294
// Basic submission
295
const onSubmit = handleSubmit((values) => {
296
console.log("Submitting:", values);
297
return fetch("/api/submit", {
298
method: "POST",
299
body: JSON.stringify(values)
300
});
301
});
302
303
// Submission with error handling
304
const onSubmitWithErrorHandling = handleSubmit(
305
async (values, { setFieldError, setErrors }) => {
306
try {
307
const response = await fetch("/api/submit", {
308
method: "POST",
309
body: JSON.stringify(values)
310
});
311
312
if (!response.ok) {
313
const errors = await response.json();
314
setErrors(errors);
315
return;
316
}
317
318
console.log("Success!");
319
} catch (error) {
320
setFieldError("", "Submission failed");
321
}
322
},
323
({ errors, results }) => {
324
console.log("Form is invalid:", errors);
325
console.log("Validation results:", results);
326
}
327
);
328
329
// Controlled form submission
330
const onControlledSubmit = handleSubmit.withControlled((values, ctx) => {
331
// Uses controlled values instead of current form values
332
console.log("Controlled values:", ctx.controlledValues);
333
return submitToAPI(ctx.controlledValues);
334
});
335
```
336
337
## Advanced Form Configuration
338
339
### Schema-based Forms
340
341
Using validation schemas for comprehensive form validation.
342
343
```typescript
344
import { useForm } from "vee-validate";
345
import * as yup from "yup";
346
347
// Yup schema
348
const yupSchema = yup.object({
349
user: yup.object({
350
name: yup.string().required(),
351
email: yup.string().email().required(),
352
profile: yup.object({
353
bio: yup.string().max(500),
354
age: yup.number().min(18)
355
})
356
})
357
});
358
359
const { handleSubmit, errors } = useForm({
360
validationSchema: yupSchema,
361
initialValues: {
362
user: {
363
name: "",
364
email: "",
365
profile: {
366
bio: "",
367
age: 0
368
}
369
}
370
}
371
});
372
373
// Raw schema with function validators
374
const rawSchema = {
375
"user.name": (value) => value ? true : "Name is required",
376
"user.email": [
377
(value) => value ? true : "Email is required",
378
(value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || "Email is invalid"
379
],
380
"user.profile.bio": (value) => value.length <= 500 || "Bio too long"
381
};
382
383
const { handleSubmit: handleRawSubmit } = useForm({
384
validationSchema: rawSchema
385
});
386
```