0
# Form Management
1
2
The FormApi class provides comprehensive form state management including validation, submission handling, field coordination, and lifecycle events. It serves as the core of TanStack React Form's state management system.
3
4
## Capabilities
5
6
### FormApi Class
7
8
Core form management class with methods for validation, submission, and field manipulation.
9
10
```typescript { .api }
11
class FormApi<
12
TFormData,
13
TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,
14
TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,
15
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
16
TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,
17
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
18
TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,
19
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
20
TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,
21
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
22
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
23
TSubmitMeta = never,
24
> {
25
/** Store instance for reactive state management */
26
store: Store<FormState<TFormData, ...>>;
27
28
/** Current form state */
29
state: FormState<TFormData, ...>;
30
31
/** Form configuration options */
32
options: FormOptions<TFormData, ...>;
33
34
/** Record of field information for each field in the form */
35
fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData>>;
36
37
constructor(opts?: FormOptions<TFormData, ...>);
38
39
/**
40
* Mounts the form and runs mount validation
41
* Call this when the form component mounts to initialize validation and event listeners
42
* @returns Cleanup function to unmount the form and remove listeners
43
*/
44
mount(): () => void;
45
46
/**
47
* Updates form options with new configuration
48
* Updates values and state if defaultValues or defaultState changed and form is pristine
49
* Re-evaluates transform function if transform deps changed
50
* @param options - Form options to update
51
*/
52
update(
53
options?: FormOptions<TFormData, ...>,
54
): void;
55
56
/**
57
* Resets the form to its initial state
58
* @param values - Optional new values to reset to (defaults to defaultValues)
59
* @param opts - Options for reset behavior
60
*/
61
reset(values?: TFormData, opts?: { keepDefaultValues?: boolean }): void;
62
63
/**
64
* Validates all fields in the form
65
* @param cause - Reason for validation (e.g., 'change', 'blur', 'submit')
66
* @returns Promise that resolves to array of validation errors
67
*/
68
validateAllFields(
69
cause: ValidationCause,
70
): Promise<ValidationError[]>;
71
72
/**
73
* Validates array field children starting from a specific index
74
* @param field - Field path to validate
75
* @param index - Starting index for validation
76
* @param cause - Reason for validation
77
* @returns Promise that resolves to array of validation errors
78
*/
79
validateArrayFieldsStartingFrom<TField extends DeepKeys<TFormData>>(
80
field: TField,
81
index: number,
82
cause: ValidationCause,
83
): Promise<ValidationError[]>;
84
85
/**
86
* Validates a specific field
87
* @param field - Field path to validate
88
* @param cause - Reason for validation
89
* @returns Promise that resolves to array of validation errors
90
*/
91
validateField<TField extends DeepKeys<TFormData>>(
92
field: TField,
93
cause: ValidationCause,
94
): Promise<ValidationError[]>;
95
96
/**
97
* Handles form submission with optional metadata
98
* @param submitMeta - Optional metadata to pass to submit handlers
99
* @returns Promise that resolves when submission completes
100
*/
101
handleSubmit(submitMeta?: TSubmitMeta): Promise<void>;
102
103
/**
104
* Gets the current value of a field
105
* @param field - Field path
106
* @returns Current field value
107
*/
108
getFieldValue<TField extends DeepKeys<TFormData>>(
109
field: TField,
110
): DeepValue<TFormData, TField>;
111
112
/**
113
* Gets the metadata for a field
114
* @param field - Field path
115
* @returns Field metadata
116
*/
117
getFieldMeta<TField extends DeepKeys<TFormData>>(
118
field: TField,
119
): FieldMeta<DeepValue<TFormData, TField>, any, any, ...> | undefined;
120
121
/**
122
* Sets the metadata for a field
123
* @param field - Field path
124
* @param updater - Function or value to update metadata
125
*/
126
setFieldMeta<TField extends DeepKeys<TFormData>>(
127
field: TField,
128
updater: Updater<FieldMeta<DeepValue<TFormData, TField>, any, any, ...>>,
129
): void;
130
131
/**
132
* Sets the value of a field
133
* @param field - Field path
134
* @param updater - Function or value to set
135
* @param opts - Options to control metadata updates and validation
136
*/
137
setFieldValue<TField extends DeepKeys<TFormData>>(
138
field: TField,
139
updater: Updater<DeepValue<TFormData, TField>>,
140
opts?: UpdateMetaOptions,
141
): void;
142
143
/**
144
* Deletes a field from the form
145
* @param field - Field path to delete
146
*/
147
deleteField<TField extends DeepKeys<TFormData>>(
148
field: TField,
149
): void;
150
151
/**
152
* Pushes a value to an array field
153
* @param field - Field path (must be an array)
154
* @param value - Value to push
155
* @param opts - Options to control metadata updates and validation
156
*/
157
pushFieldValue<TField extends DeepKeys<TFormData>>(
158
field: TField,
159
value: DeepValue<TFormData, TField> extends Array<infer U> ? U : never,
160
opts?: UpdateMetaOptions,
161
): void;
162
163
/**
164
* Inserts a value at a specific index in an array field
165
* @param field - Field path (must be an array)
166
* @param index - Index at which to insert
167
* @param value - Value to insert
168
* @param opts - Options to control metadata updates and validation
169
*/
170
insertFieldValue<TField extends DeepKeys<TFormData>>(
171
field: TField,
172
index: number,
173
value: DeepValue<TFormData, TField> extends Array<infer U> ? U : never,
174
opts?: UpdateMetaOptions,
175
): Promise<void>;
176
177
/**
178
* Replaces a value at a specific index in an array field
179
* @param field - Field path (must be an array)
180
* @param index - Index to replace
181
* @param value - New value
182
* @param opts - Options to control metadata updates and validation
183
*/
184
replaceFieldValue<TField extends DeepKeys<TFormData>>(
185
field: TField,
186
index: number,
187
value: DeepValue<TFormData, TField> extends Array<infer U> ? U : never,
188
opts?: UpdateMetaOptions,
189
): Promise<void>;
190
191
/**
192
* Removes a value at a specific index from an array field
193
* @param field - Field path (must be an array)
194
* @param index - Index to remove
195
* @param opts - Options to control metadata updates and validation
196
*/
197
removeFieldValue<TField extends DeepKeys<TFormData>>(
198
field: TField,
199
index: number,
200
opts?: UpdateMetaOptions,
201
): Promise<void>;
202
203
/**
204
* Swaps two values in an array field
205
* @param field - Field path (must be an array)
206
* @param index1 - First index
207
* @param index2 - Second index
208
* @param opts - Options to control metadata updates and validation
209
*/
210
swapFieldValues<TField extends DeepKeys<TFormData>>(
211
field: TField,
212
index1: number,
213
index2: number,
214
opts?: UpdateMetaOptions,
215
): void;
216
217
/**
218
* Moves a value from one index to another in an array field
219
* @param field - Field path (must be an array)
220
* @param index1 - Source index
221
* @param index2 - Destination index
222
* @param opts - Options to control metadata updates and validation
223
*/
224
moveFieldValues<TField extends DeepKeys<TFormData>>(
225
field: TField,
226
index1: number,
227
index2: number,
228
opts?: UpdateMetaOptions,
229
): void;
230
231
/**
232
* Clears all values from an array field
233
* @param field - Field path (must be an array)
234
* @param opts - Options to control metadata updates and validation
235
*/
236
clearFieldValues<TField extends DeepKeys<TFormData>>(
237
field: TField,
238
opts?: UpdateMetaOptions,
239
): void;
240
241
/**
242
* Resets a field to its default value
243
* @param field - Field path to reset
244
*/
245
resetField<TField extends DeepKeys<TFormData>>(
246
field: TField,
247
): void;
248
249
/**
250
* Gets the field info of the specified field including instance and validation metadata
251
* @param field - Field path to get info for
252
* @returns FieldInfo containing field instance and validation metadata
253
*/
254
getFieldInfo<TField extends DeepKeys<TFormData>>(
255
field: TField,
256
): FieldInfo<TFormData>;
257
258
/**
259
* Updates the form's errorMap directly
260
* Useful for setting server-side validation errors
261
* @param errorMap - New error map to set
262
*/
263
setErrorMap(
264
errorMap: FormValidationErrorMap<
265
TFormData,
266
UnwrapFormValidateOrFn<TOnMount>,
267
UnwrapFormValidateOrFn<TOnChange>,
268
UnwrapFormAsyncValidateOrFn<TOnChangeAsync>,
269
UnwrapFormValidateOrFn<TOnBlur>,
270
UnwrapFormAsyncValidateOrFn<TOnBlurAsync>,
271
UnwrapFormValidateOrFn<TOnSubmit>,
272
UnwrapFormAsyncValidateOrFn<TOnSubmitAsync>,
273
UnwrapFormValidateOrFn<TOnDynamic>,
274
UnwrapFormAsyncValidateOrFn<TOnDynamicAsync>,
275
UnwrapFormAsyncValidateOrFn<TOnServer>
276
>,
277
): void;
278
279
/**
280
* Returns both form-level and field-level errors
281
* Provides a complete view of all validation errors in the form
282
* @returns Object containing form errors and field errors
283
*/
284
getAllErrors(): {
285
form: {
286
errors: Array<
287
| UnwrapFormValidateOrFn<TOnMount>
288
| UnwrapFormAsyncValidateOrFn<TOnChangeAsync>
289
| UnwrapFormValidateOrFn<TOnChange>
290
| UnwrapFormValidateOrFn<TOnBlur>
291
| UnwrapFormAsyncValidateOrFn<TOnBlurAsync>
292
| UnwrapFormValidateOrFn<TOnSubmit>
293
| UnwrapFormAsyncValidateOrFn<TOnSubmitAsync>
294
| UnwrapFormValidateOrFn<TOnDynamic>
295
| UnwrapFormAsyncValidateOrFn<TOnDynamicAsync>
296
| UnwrapFormAsyncValidateOrFn<TOnServer>
297
>;
298
errorMap: ValidationErrorMap<
299
| UnwrapFormValidateOrFn<TOnMount>
300
| UnwrapFormAsyncValidateOrFn<TOnChangeAsync>
301
| UnwrapFormValidateOrFn<TOnChange>
302
| UnwrapFormValidateOrFn<TOnBlur>
303
| UnwrapFormAsyncValidateOrFn<TOnBlurAsync>
304
| UnwrapFormValidateOrFn<TOnSubmit>
305
| UnwrapFormAsyncValidateOrFn<TOnSubmitAsync>
306
| UnwrapFormValidateOrFn<TOnDynamic>
307
| UnwrapFormAsyncValidateOrFn<TOnDynamicAsync>
308
| UnwrapFormAsyncValidateOrFn<TOnServer>
309
>;
310
};
311
fields: Record<
312
DeepKeys<TFormData>,
313
{ errors: ValidationError[]; errorMap: ValidationErrorMap }
314
>;
315
};
316
317
/**
318
* Parses form values with a Standard Schema without setting internal errors
319
* Useful for one-off validation checks without affecting form state
320
* @param schema - The Standard Schema to validate against
321
* @returns Validation error if any, undefined if valid
322
*/
323
parseValuesWithSchema(
324
schema: StandardSchemaV1<TFormData, unknown>,
325
): ValidationError | undefined;
326
327
/**
328
* Async version of parseValuesWithSchema
329
* Parses form values with a Standard Schema without setting internal errors
330
* @param schema - The Standard Schema to validate against
331
* @returns Promise resolving to validation error if any, undefined if valid
332
*/
333
parseValuesWithSchemaAsync(
334
schema: StandardSchemaV1<TFormData, unknown>,
335
): Promise<ValidationError | undefined>;
336
337
/**
338
* Gets the unique form identifier
339
* Useful for debugging and devtools integration
340
*/
341
get formId(): string;
342
}
343
```
344
345
### ReactFormExtendedApi
346
347
React-specific extension of FormApi with Field component and Subscribe function.
348
349
```typescript { .api }
350
type ReactFormExtendedApi<
351
TFormData,
352
TOnMount extends undefined | FormValidateOrFn<TFormData>,
353
TOnChange extends undefined | FormValidateOrFn<TFormData>,
354
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
355
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
356
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
357
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
358
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
359
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
360
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
361
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
362
TSubmitMeta,
363
> = FormApi<
364
TFormData,
365
TOnMount,
366
TOnChange,
367
TOnChangeAsync,
368
TOnBlur,
369
TOnBlurAsync,
370
TOnSubmit,
371
TOnSubmitAsync,
372
TOnDynamic,
373
TOnDynamicAsync,
374
TOnServer,
375
TSubmitMeta
376
> & {
377
/**
378
* A React component to render form fields
379
* Automatically bound to this form instance
380
*/
381
Field: FieldComponent<
382
TFormData,
383
TOnMount,
384
TOnChange,
385
TOnChangeAsync,
386
TOnBlur,
387
TOnBlurAsync,
388
TOnSubmit,
389
TOnSubmitAsync,
390
TOnDynamic,
391
TOnDynamicAsync,
392
TOnServer,
393
TSubmitMeta
394
>;
395
396
/**
397
* A Subscribe function to listen and react to form state changes
398
* Useful for side effects or conditional rendering based on state
399
*/
400
Subscribe: <TSelected = FormState<TFormData, ...>>(props: {
401
selector?: (state: FormState<TFormData, ...>) => TSelected;
402
children: ((state: TSelected) => ReactNode) | ReactNode;
403
}) => ReactNode;
404
};
405
```
406
407
### FormOptions
408
409
Configuration options for creating a form instance.
410
411
```typescript { .api }
412
interface FormOptions<
413
TFormData,
414
TOnMount extends undefined | FormValidateOrFn<TFormData>,
415
TOnChange extends undefined | FormValidateOrFn<TFormData>,
416
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
417
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
418
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
419
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
420
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
421
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
422
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
423
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
424
TSubmitMeta,
425
> extends BaseFormOptions<TFormData, TSubmitMeta> {
426
/** Form-level validators */
427
validators?: FormValidators<
428
TFormData,
429
TOnMount,
430
TOnChange,
431
TOnChangeAsync,
432
TOnBlur,
433
TOnBlurAsync,
434
TOnSubmit,
435
TOnSubmitAsync,
436
TOnDynamic,
437
TOnDynamicAsync,
438
TOnServer
439
>;
440
441
/** Form transformation configuration */
442
transform?: FormTransform<
443
TFormData,
444
TOnMount,
445
TOnChange,
446
TOnChangeAsync,
447
TOnBlur,
448
TOnBlurAsync,
449
TOnSubmit,
450
TOnSubmitAsync,
451
TOnDynamic,
452
TOnDynamicAsync,
453
TOnServer,
454
TSubmitMeta
455
>;
456
457
/** Form identifier used for devtools and debugging */
458
formId?: string;
459
460
/** If true, allows form submission even when validation fails */
461
canSubmitWhenInvalid?: boolean;
462
463
/** Debounce time in milliseconds for async validation */
464
asyncDebounceMs?: number;
465
466
/** Whether to always run async validation */
467
asyncAlways?: boolean;
468
469
/** Initial form values */
470
defaultValues?: TFormData;
471
472
/** Initial form state */
473
defaultState?: Partial<FormState<TFormData, ...>>;
474
475
/** Custom validation logic function */
476
validationLogic?: ValidationLogicFn;
477
478
/**
479
* Submit handler called when form is valid
480
* @param props - Object containing form value, API instance, and optional metadata
481
*/
482
onSubmit?: (props: {
483
value: TFormData;
484
formApi: FormApi<TFormData, ...>;
485
meta?: TSubmitMeta;
486
}) => any | Promise<any>;
487
488
/**
489
* Handler called when form submission is attempted but form is invalid
490
* @param props - Object containing form API instance
491
*/
492
onSubmitInvalid?: (props: {
493
value: TFormData;
494
formApi: FormApi<TFormData, ...>;
495
}) => void;
496
497
/** Form-level lifecycle event listeners */
498
listeners?: FormListeners<
499
TFormData,
500
TOnMount,
501
TOnChange,
502
TOnChangeAsync,
503
TOnBlur,
504
TOnBlurAsync,
505
TOnSubmit,
506
TOnSubmitAsync,
507
TOnDynamic,
508
TOnDynamicAsync,
509
TOnServer,
510
TSubmitMeta
511
>;
512
}
513
514
interface BaseFormOptions<TFormData, TSubmitMeta> {
515
/** Initial form values */
516
defaultValues?: TFormData;
517
518
/** Metadata passed from handleSubmit to onSubmit function */
519
onSubmitMeta?: TSubmitMeta;
520
}
521
```
522
523
### FormState
524
525
Complete form state interface with base and derived properties.
526
527
```typescript { .api }
528
interface FormState<
529
TFormData,
530
TOnMount extends undefined | FormValidateOrFn<TFormData>,
531
TOnChange extends undefined | FormValidateOrFn<TFormData>,
532
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
533
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
534
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
535
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
536
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
537
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
538
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
539
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
540
> extends BaseFormState<TFormData, ...>,
541
DerivedFormState<TFormData, ...> {}
542
543
interface BaseFormState<TFormData, ...> {
544
/** Current form values */
545
values: TFormData;
546
547
/** Map of form-level validation errors by trigger type */
548
errorMap: ValidationErrorMap<...>;
549
550
/** Metadata for validation execution by trigger type */
551
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>;
552
553
/** Record of base field metadata for each field (not including derived properties) */
554
fieldMetaBase: Record<DeepKeys<TFormData>, AnyFieldMetaBase>;
555
556
/** Whether the form is currently submitting (after handleSubmit, before onSubmit completes) */
557
isSubmitting: boolean;
558
559
/** Whether the onSubmit function has completed successfully */
560
isSubmitted: boolean;
561
562
/** Whether the form or any fields are currently validating */
563
isValidating: boolean;
564
565
/** Number of times submission has been attempted */
566
submissionAttempts: number;
567
568
/** Whether the last submission was successful */
569
isSubmitSuccessful: boolean;
570
}
571
572
interface DerivedFormState<TFormData, ...> {
573
/** Form-level validation errors array */
574
errors: ValidationError[];
575
576
/** Map of field metadata with derived properties keyed by field path */
577
fieldMeta: Record<DeepKeys<TFormData>, FieldMeta<any, any, any, ...>>;
578
579
/** Whether the form can be submitted (valid and not submitting) */
580
canSubmit: boolean;
581
582
/** Whether any field has been touched */
583
isTouched: boolean;
584
585
/** Whether the form is in its initial state */
586
isPristine: boolean;
587
588
/** Whether the form has been modified from initial state */
589
isDirty: boolean;
590
591
/** Whether the form is valid (no errors at form or field level) */
592
isValid: boolean;
593
}
594
```
595
596
### FormValidators
597
598
Form-level validator configuration.
599
600
```typescript { .api }
601
interface FormValidators<
602
TFormData,
603
TOnMount extends undefined | FormValidateOrFn<TFormData>,
604
TOnChange extends undefined | FormValidateOrFn<TFormData>,
605
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
606
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
607
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
608
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
609
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
610
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
611
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
612
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
613
> {
614
/** Validator that runs when form is mounted */
615
onMount?: TOnMount;
616
617
/** Validator that runs when form values change */
618
onChange?: TOnChange;
619
620
/** Async validator that runs when form values change */
621
onChangeAsync?: TOnChangeAsync;
622
623
/** Validator that runs when a field is blurred */
624
onBlur?: TOnBlur;
625
626
/** Async validator that runs when a field is blurred */
627
onBlurAsync?: TOnBlurAsync;
628
629
/** Validator that runs on form submission */
630
onSubmit?: TOnSubmit;
631
632
/** Async validator that runs on form submission */
633
onSubmitAsync?: TOnSubmitAsync;
634
635
/** Validator that runs dynamically based on validation logic */
636
onDynamic?: TOnDynamic;
637
638
/** Async validator that runs dynamically based on validation logic */
639
onDynamicAsync?: TOnDynamicAsync;
640
641
/** Server-side validator (used with framework integrations) */
642
onServer?: TOnServer;
643
}
644
```
645
646
### FormListeners
647
648
Event listener configuration for form lifecycle events.
649
650
```typescript { .api }
651
interface FormListeners<TFormData, ...> {
652
/**
653
* Listener called when form is mounted
654
* @param props.formApi - Form API instance
655
*/
656
onMount?: (props: {
657
formApi: FormApi<TFormData, ...>;
658
}) => void;
659
660
/**
661
* Listener called when form values change
662
* @param props.formApi - Form API instance
663
* @param props.fieldApi - Field API that triggered the change
664
*/
665
onChange?: (props: {
666
formApi: FormApi<TFormData, ...>;
667
fieldApi: AnyFieldApi;
668
}) => void;
669
670
/** Debounce time in milliseconds for onChange listener */
671
onChangeDebounceMs?: number;
672
673
/**
674
* Listener called when a field is blurred
675
* @param props.formApi - Form API instance
676
* @param props.fieldApi - Field API that was blurred
677
*/
678
onBlur?: (props: {
679
formApi: FormApi<TFormData, ...>;
680
fieldApi: AnyFieldApi;
681
}) => void;
682
683
/** Debounce time in milliseconds for onBlur listener */
684
onBlurDebounceMs?: number;
685
686
/**
687
* Listener called on form submission
688
* @param props.formApi - Form API instance
689
* @param props.meta - Submit metadata
690
*/
691
onSubmit?: (props: {
692
formApi: FormApi<TFormData, ...>;
693
meta: TSubmitMeta;
694
}) => void;
695
}
696
```
697
698
### Form Validation Types
699
700
```typescript { .api }
701
/**
702
* Synchronous form validation function
703
* @param props.value - Current form values
704
* @param props.formApi - Form API instance
705
* @returns Validation error or undefined if valid
706
*/
707
type FormValidateFn<TFormData> = (props: {
708
value: TFormData;
709
formApi: FormApi<TFormData, ...>;
710
}) => ValidationError | Promise<ValidationError>;
711
712
/**
713
* Asynchronous form validation function with abort signal support
714
* @param props.value - Current form values
715
* @param props.signal - AbortSignal for cancellation
716
* @param props.formApi - Form API instance
717
* @returns Promise resolving to validation error or undefined if valid
718
*/
719
type FormValidateAsyncFn<TFormData> = (props: {
720
value: TFormData;
721
signal: AbortSignal;
722
formApi: FormApi<TFormData, ...>;
723
}) => ValidationError | Promise<ValidationError>;
724
725
/** Union of validation function or Standard Schema validator */
726
type FormValidateOrFn<TFormData> =
727
| FormValidateFn<TFormData>
728
| StandardSchemaV1<TFormData, TFormData>;
729
730
/** Union of async validation function or Standard Schema validator */
731
type FormAsyncValidateOrFn<TFormData> =
732
| FormValidateAsyncFn<TFormData>
733
| StandardSchemaV1<TFormData, TFormData>;
734
```
735
736
### Validation Metadata Types
737
738
Types for tracking validation state and field information.
739
740
```typescript { .api }
741
/**
742
* Validation metadata for tracking async validation state
743
* Stores an abort controller to cancel previous async validation attempts
744
*/
745
type ValidationMeta = {
746
/** AbortController stored in memory to cancel previous async validation attempts */
747
lastAbortController: AbortController;
748
};
749
750
/**
751
* Field information object containing field instance and validation metadata
752
* Accessible via form.fieldInfo[fieldName]
753
*/
754
type FieldInfo<TFormData> = {
755
/** Field API instance or null if field is not currently mounted */
756
instance: FieldApi<TFormData, any, any, ...> | null;
757
758
/** Validation metadata by trigger type for this field */
759
validationMetaMap: Record<ValidationErrorMapKeys, ValidationMeta | undefined>;
760
};
761
```
762
763
## Helper Types
764
765
Type aliases for convenience when working with forms without needing to specify all generic parameters.
766
767
```typescript { .api }
768
/**
769
* FormApi with all generics set to any for convenience in dynamic or loosely-typed contexts
770
* Useful when you need to work with forms of unknown structure
771
*/
772
type AnyFormApi = FormApi<any, any, any, any, any, any, any, any, any, any, any, any>;
773
774
/**
775
* FormState with all generics set to any for convenience
776
* Useful for generic form state handling
777
*/
778
type AnyFormState = FormState<any, any, any, any, any, any, any, any, any, any, any>;
779
780
/**
781
* FormOptions with all generics set to any for convenience
782
* Useful when passing form options dynamically
783
*/
784
type AnyFormOptions = FormOptions<any, any, any, any, any, any, any, any, any, any, any, any>;
785
```
786
787
## Usage Examples
788
789
### Basic Form Creation
790
791
```typescript
792
import { useForm } from '@tanstack/react-form';
793
794
function ContactForm() {
795
const form = useForm({
796
defaultValues: {
797
name: '',
798
email: '',
799
message: '',
800
},
801
validators: {
802
onChange: ({ value }) => {
803
if (!value.email.includes('@')) {
804
return { form: 'Invalid email' };
805
}
806
return undefined;
807
},
808
},
809
onSubmit: async ({ value }) => {
810
await fetch('/api/contact', {
811
method: 'POST',
812
body: JSON.stringify(value),
813
});
814
},
815
});
816
817
return (
818
<form onSubmit={(e) => {
819
e.preventDefault();
820
form.handleSubmit();
821
}}>
822
{/* Field components */}
823
</form>
824
);
825
}
826
```
827
828
### Async Validation with Debouncing
829
830
```typescript
831
const form = useForm({
832
defaultValues: {
833
username: '',
834
},
835
asyncDebounceMs: 500,
836
validators: {
837
onChangeAsync: async ({ value, signal }) => {
838
const response = await fetch(
839
`/api/check-username?username=${value.username}`,
840
{ signal }
841
);
842
const data = await response.json();
843
return data.available ? undefined : 'Username already taken';
844
},
845
},
846
});
847
```
848
849
### Form State Subscription
850
851
```typescript
852
function FormStatus() {
853
const form = useForm({ /* ... */ });
854
855
return (
856
<form.Subscribe
857
selector={(state) => ({
858
canSubmit: state.canSubmit,
859
isSubmitting: state.isSubmitting,
860
errors: state.errors,
861
})}
862
>
863
{({ canSubmit, isSubmitting, errors }) => (
864
<div>
865
<button type="submit" disabled={!canSubmit || isSubmitting}>
866
{isSubmitting ? 'Submitting...' : 'Submit'}
867
</button>
868
{errors.map((error, i) => (
869
<div key={i}>{String(error)}</div>
870
))}
871
</div>
872
)}
873
</form.Subscribe>
874
);
875
}
876
```
877
878
### Array Field Manipulation
879
880
```typescript
881
const form = useForm({
882
defaultValues: {
883
todos: [{ text: '', completed: false }],
884
},
885
});
886
887
// Add new todo
888
form.pushFieldValue('todos', { text: '', completed: false });
889
890
// Remove todo at index
891
form.removeFieldValue('todos', 1);
892
893
// Move todo from index 0 to index 2
894
form.moveFieldValues('todos', 0, 2);
895
896
// Clear all todos
897
form.clearFieldValues('todos');
898
```
899