0
# Field Management
1
2
The FieldApi class provides granular control over individual form fields, including validation, metadata tracking, and array field operations. It integrates seamlessly with the FormApi to maintain field state and coordinate validation.
3
4
## Capabilities
5
6
### FieldApi Class
7
8
Core field management class for individual form field state and operations.
9
10
```typescript { .api }
11
class FieldApi<
12
TParentData,
13
TName extends DeepKeys<TParentData>,
14
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
15
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
16
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
17
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
18
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
19
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
20
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
21
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
22
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
23
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
24
TFormOnMount extends undefined | FormValidateOrFn<TParentData> = undefined,
25
TFormOnChange extends undefined | FormValidateOrFn<TParentData> = undefined,
26
TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
27
TFormOnBlur extends undefined | FormValidateOrFn<TParentData> = undefined,
28
TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
29
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData> = undefined,
30
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
31
TFormOnDynamic extends undefined | FormValidateOrFn<TParentData> = undefined,
32
TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
33
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
34
TParentSubmitMeta = never,
35
> {
36
/** Store instance for reactive state management */
37
store: Store<FieldState<TData>>;
38
39
/** Current field state */
40
state: FieldState<TData>;
41
42
/** Field name/path in the form data structure */
43
name: TName;
44
45
/** Parent form API instance */
46
form: FormApi<TParentData, ...>;
47
48
/** Field configuration options */
49
options: FieldApiOptions<TParentData, TName, TData, ...>;
50
51
constructor(opts: FieldApiOptions<TParentData, TName, TData, ...>);
52
53
/**
54
* Mounts the field and runs mount validation
55
* @returns Cleanup function to unmount the field
56
*/
57
mount(): () => void;
58
59
/**
60
* Updates field options with new configuration
61
* @param opts - Partial field options to update
62
*/
63
update(
64
opts: FieldApiOptions<TParentData, TName, TData, ...>,
65
): void;
66
67
/**
68
* Gets the current field value
69
* @deprecated Use `field.state.value` instead
70
* @returns Current value of the field
71
*/
72
getValue(): TData;
73
74
/**
75
* Sets the field value
76
* @param updater - Function or value to set
77
* @param opts - Options to control metadata updates and validation
78
*/
79
setValue(
80
updater: Updater<TData>,
81
opts?: UpdateMetaOptions,
82
): void;
83
84
/**
85
* Gets the field metadata
86
* @returns Current field metadata
87
*/
88
getMeta(): FieldMeta<TData, ...>;
89
90
/**
91
* Sets the field metadata
92
* @param updater - Function or value to update metadata
93
*/
94
setMeta(
95
updater: Updater<FieldMeta<TData, ...>>,
96
): void;
97
98
/**
99
* Gets field information including instance and validation metadata
100
* @returns Field info object
101
*/
102
getInfo(): FieldInfo;
103
104
/**
105
* Pushes a value to the field (for array fields)
106
* @param value - Value to push
107
* @param opts - Options to control metadata updates and validation
108
*/
109
pushValue(
110
value: TData extends Array<infer U> ? U : never,
111
opts?: UpdateMetaOptions,
112
): void;
113
114
/**
115
* Inserts a value at a specific index (for array fields)
116
* @param index - Index at which to insert
117
* @param value - Value to insert
118
* @param opts - Options to control metadata updates and validation
119
*/
120
insertValue(
121
index: number,
122
value: TData extends Array<infer U> ? U : never,
123
opts?: UpdateMetaOptions,
124
): void;
125
126
/**
127
* Removes a value at a specific index (for array fields)
128
* @param index - Index to remove
129
* @param opts - Options to control metadata updates and validation
130
*/
131
removeValue(
132
index: number,
133
opts?: UpdateMetaOptions,
134
): void;
135
136
/**
137
* Replaces a value at a specific index (for array fields)
138
* @param index - Index to replace
139
* @param value - New value
140
* @param opts - Options to control metadata updates and validation
141
*/
142
replaceValue(
143
index: number,
144
value: TData extends Array<infer U> ? U : never,
145
opts?: UpdateMetaOptions,
146
): void;
147
148
/**
149
* Swaps two values in the array (for array fields)
150
* @param aIndex - First index
151
* @param bIndex - Second index
152
* @param opts - Options to control metadata updates and validation
153
*/
154
swapValues(
155
aIndex: number,
156
bIndex: number,
157
opts?: UpdateMetaOptions,
158
): void;
159
160
/**
161
* Moves a value from one index to another (for array fields)
162
* @param aIndex - Source index
163
* @param bIndex - Destination index
164
* @param opts - Options to control metadata updates and validation
165
*/
166
moveValue(
167
aIndex: number,
168
bIndex: number,
169
opts?: UpdateMetaOptions,
170
): void;
171
172
/**
173
* Clears all values from the field (for array fields)
174
* @param opts - Options to control metadata updates and validation
175
*/
176
clearValues(
177
opts?: UpdateMetaOptions,
178
): void;
179
180
/**
181
* Validates the field
182
* @param cause - Reason for validation
183
* @returns Promise that resolves to array of validation errors
184
*/
185
validate(
186
cause: ValidationCause,
187
): Promise<ValidationError[]>;
188
189
/**
190
* Updates the field's error map by merging with existing errors
191
* @param errorMap - Validation error map to merge
192
*/
193
setErrorMap(
194
errorMap: ValidationErrorMap<
195
UnwrapFieldValidateOrFn<TName, TOnMount, TFormOnMount>,
196
UnwrapFieldValidateOrFn<TName, TOnChange, TFormOnChange>,
197
UnwrapFieldAsyncValidateOrFn<TName, TOnChangeAsync, TFormOnChangeAsync>,
198
UnwrapFieldValidateOrFn<TName, TOnBlur, TFormOnBlur>,
199
UnwrapFieldAsyncValidateOrFn<TName, TOnBlurAsync, TFormOnBlurAsync>,
200
UnwrapFieldValidateOrFn<TName, TOnSubmit, TFormOnSubmit>,
201
UnwrapFieldAsyncValidateOrFn<TName, TOnSubmitAsync, TFormOnSubmitAsync>,
202
UnwrapFieldValidateOrFn<TName, TOnDynamic, TFormOnDynamic>,
203
UnwrapFieldAsyncValidateOrFn<TName, TOnDynamicAsync, TFormOnDynamicAsync>
204
>,
205
): void;
206
207
/**
208
* Parses field value with a Standard Schema without setting internal errors
209
* Useful for one-off validation checks without affecting field state
210
* @param schema - The Standard Schema to validate against
211
* @returns Validation error if any, undefined if valid
212
*/
213
parseValueWithSchema(
214
schema: StandardSchemaV1<TData, unknown>,
215
): ValidationError | undefined;
216
217
/**
218
* Async version of parseValueWithSchema
219
* Parses field value with a Standard Schema without setting internal errors
220
* @param schema - The Standard Schema to validate against
221
* @returns Promise resolving to validation error if any, undefined if valid
222
*/
223
parseValueWithSchemaAsync(
224
schema: StandardSchemaV1<TData, unknown>,
225
): Promise<ValidationError | undefined>;
226
227
/**
228
* Handles field value change
229
* @param updater - Function or value to set
230
*/
231
handleChange(
232
updater: Updater<TData>,
233
): void;
234
235
/**
236
* Handles field blur event
237
*/
238
handleBlur(): void;
239
}
240
```
241
242
### Field Component
243
244
React component for rendering fields with render prop pattern.
245
246
```typescript { .api }
247
/**
248
* A function component that takes field options and a render function as children
249
* Uses the useField hook internally to manage the field instance
250
*
251
* @param props.name - Field path in the form data structure
252
* @param props.validators - Field-level validators
253
* @param props.children - Render function that receives the field API
254
* @param props.defaultValue - Default value for the field
255
* @param props.asyncDebounceMs - Debounce time for async validation
256
* @param props.preserveValue - Whether to preserve value on unmount
257
* @param props.mode - Rendering mode ('value' | 'array')
258
* @returns ReactNode
259
*/
260
const Field: <
261
TParentData,
262
TName extends DeepKeys<TParentData>,
263
TData extends DeepValue<TParentData, TName>,
264
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
265
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
266
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
267
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
268
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
269
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
270
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
271
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
272
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
273
TFormOnMount extends undefined | FormValidateOrFn<TParentData> = undefined,
274
TFormOnChange extends undefined | FormValidateOrFn<TParentData> = undefined,
275
TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
276
TFormOnBlur extends undefined | FormValidateOrFn<TParentData> = undefined,
277
TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
278
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData> = undefined,
279
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
280
TFormOnDynamic extends undefined | FormValidateOrFn<TParentData> = undefined,
281
TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
282
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
283
TParentSubmitMeta = never,
284
>({
285
children,
286
...fieldOptions
287
}: FieldComponentProps<
288
TParentData,
289
TName,
290
TData,
291
TOnMount,
292
TOnChange,
293
TOnChangeAsync,
294
TOnBlur,
295
TOnBlurAsync,
296
TOnSubmit,
297
TOnSubmitAsync,
298
TOnDynamic,
299
TOnDynamicAsync,
300
TFormOnMount,
301
TFormOnChange,
302
TFormOnChangeAsync,
303
TFormOnBlur,
304
TFormOnBlurAsync,
305
TFormOnSubmit,
306
TFormOnSubmitAsync,
307
TFormOnDynamic,
308
TFormOnDynamicAsync,
309
TFormOnServer,
310
TParentSubmitMeta
311
>) => ReactNode;
312
```
313
314
### FieldOptions
315
316
Configuration options for creating a field instance.
317
318
```typescript { .api }
319
interface FieldOptions<
320
TParentData,
321
TName extends DeepKeys<TParentData>,
322
TData extends DeepValue<TParentData, TName>,
323
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
324
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
325
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
326
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
327
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
328
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
329
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
330
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
331
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
332
> {
333
/** Field path in the form data structure (supports dot notation and array indices) */
334
name: TName;
335
336
/** Default value for the field */
337
defaultValue?: TData;
338
339
/** Debounce time in milliseconds for async validation */
340
asyncDebounceMs?: number;
341
342
/** Whether to always run async validation (even if sync validation fails) */
343
asyncAlways?: boolean;
344
345
/** Field-level validators */
346
validators?: FieldValidators<
347
TParentData,
348
TName,
349
TData,
350
TOnMount,
351
TOnChange,
352
TOnChangeAsync,
353
TOnBlur,
354
TOnBlurAsync,
355
TOnSubmit,
356
TOnSubmitAsync,
357
TOnDynamic,
358
TOnDynamicAsync
359
>;
360
361
/** Whether to preserve field value when unmounted */
362
preserveValue?: boolean;
363
}
364
365
interface FieldApiOptions<
366
TParentData,
367
TName extends DeepKeys<TParentData>,
368
TData extends DeepValue<TParentData, TName>,
369
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
370
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
371
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
372
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
373
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
374
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
375
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
376
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
377
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
378
TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
379
TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
380
TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
381
TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
382
TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
383
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
384
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
385
TFormOnDynamic extends undefined | FormValidateOrFn<TParentData>,
386
TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
387
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
388
TParentSubmitMeta,
389
> extends FieldOptions<
390
TParentData,
391
TName,
392
TData,
393
TOnMount,
394
TOnChange,
395
TOnChangeAsync,
396
TOnBlur,
397
TOnBlurAsync,
398
TOnSubmit,
399
TOnSubmitAsync,
400
TOnDynamic,
401
TOnDynamicAsync
402
> {
403
/** Parent form API instance */
404
form: FormApi<
405
TParentData,
406
TFormOnMount,
407
TFormOnChange,
408
TFormOnChangeAsync,
409
TFormOnBlur,
410
TFormOnBlurAsync,
411
TFormOnSubmit,
412
TFormOnSubmitAsync,
413
TFormOnDynamic,
414
TFormOnDynamicAsync,
415
TFormOnServer,
416
TParentSubmitMeta
417
>;
418
}
419
```
420
421
### UseFieldOptions
422
423
React hook-specific field options with mode support.
424
425
```typescript { .api }
426
interface UseFieldOptions<
427
TParentData,
428
TName extends DeepKeys<TParentData>,
429
TData extends DeepValue<TParentData, TName>,
430
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
431
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
432
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
433
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
434
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
435
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
436
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
437
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
438
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
439
TFormOnMount extends undefined | FormValidateOrFn<TParentData>,
440
TFormOnChange extends undefined | FormValidateOrFn<TParentData>,
441
TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
442
TFormOnBlur extends undefined | FormValidateOrFn<TParentData>,
443
TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
444
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
445
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
446
TFormOnDynamic extends undefined | FormValidateOrFn<TParentData>,
447
TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
448
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
449
TSubmitMeta,
450
> extends FieldApiOptions<
451
TParentData,
452
TName,
453
TData,
454
TOnMount,
455
TOnChange,
456
TOnChangeAsync,
457
TOnBlur,
458
TOnBlurAsync,
459
TOnSubmit,
460
TOnSubmitAsync,
461
TOnDynamic,
462
TOnDynamicAsync,
463
TFormOnMount,
464
TFormOnChange,
465
TFormOnChangeAsync,
466
TFormOnBlur,
467
TFormOnBlurAsync,
468
TFormOnSubmit,
469
TFormOnSubmitAsync,
470
TFormOnDynamic,
471
TFormOnDynamicAsync,
472
TFormOnServer,
473
TSubmitMeta
474
> {
475
/**
476
* Rendering mode for the field
477
* - 'value': Standard value mode (default) - field re-renders on every value change
478
* - 'array': Array mode with optimized re-rendering - use for array fields to prevent unnecessary re-renders of sibling array items when one item changes
479
*
480
* When using 'array' mode with array fields, child fields will only re-render when their specific index changes,
481
* rather than re-rendering all items when any item in the array changes. This significantly improves performance
482
* for forms with dynamic arrays of fields.
483
*
484
* Example usage:
485
* ```typescript
486
* <form.Field name="todos" mode="array">
487
* {(field) => (
488
* <div>
489
* {field.state.value.map((_, index) => (
490
* <form.Field key={index} name={`todos[${index}].text`}>
491
* {(subField) => <input value={subField.state.value} />}
492
* </form.Field>
493
* ))}
494
* </div>
495
* )}
496
* </form.Field>
497
* ```
498
*/
499
mode?: 'value' | 'array';
500
}
501
502
interface UseFieldOptionsBound<
503
TParentData,
504
TName extends DeepKeys<TParentData>,
505
TData extends DeepValue<TParentData, TName>,
506
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
507
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
508
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
509
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
510
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
511
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
512
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
513
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
514
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
515
> extends FieldOptions<
516
TParentData,
517
TName,
518
TData,
519
TOnMount,
520
TOnChange,
521
TOnChangeAsync,
522
TOnBlur,
523
TOnBlurAsync,
524
TOnSubmit,
525
TOnSubmitAsync,
526
TOnDynamic,
527
TOnDynamicAsync
528
> {
529
/** Rendering mode for the field */
530
mode?: 'value' | 'array';
531
}
532
```
533
534
### FieldState
535
536
Complete field state including value and metadata.
537
538
```typescript { .api }
539
interface FieldState<TData> {
540
/** Current field value */
541
value: TData;
542
543
/** Field metadata (base + derived) */
544
meta: FieldMeta<TData, any, any, ...>;
545
}
546
547
type FieldMeta<TData, ...> = FieldMetaBase<TData, ...> & FieldMetaDerived<TData, ...>;
548
549
interface FieldMetaBase<TData, ...> {
550
/** Whether the field has been touched (focused and then blurred) */
551
isTouched: boolean;
552
553
/** Whether the field has been blurred */
554
isBlurred: boolean;
555
556
/** Whether the field has been modified from its initial value */
557
isDirty: boolean;
558
559
/** Map of errors by validation trigger type */
560
errorMap: ValidationErrorMap;
561
562
/** Map tracking the source of errors (private) */
563
errorSourceMap: ValidationErrorMapSource;
564
565
/** Whether validation is currently in progress */
566
isValidating: boolean;
567
}
568
569
interface FieldMetaDerived<TData, ...> {
570
/** Array of validation errors for the field */
571
errors: ValidationError[];
572
573
/**
574
* Array of validation errors only shown after field has been touched/blurred
575
* This property may be undefined until the field has been interacted with
576
*/
577
touchedErrors?: ValidationError[];
578
579
/** Whether the field is in its initial state */
580
isPristine: boolean;
581
582
/** Whether the field is valid (no errors) */
583
isValid: boolean;
584
585
/** Whether the field's current value is the default value */
586
isDefaultValue: boolean;
587
}
588
589
/** Type representing any FieldMeta */
590
type AnyFieldMeta = FieldMeta<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
591
```
592
593
### FieldValidators
594
595
Field-level validator configuration.
596
597
```typescript { .api }
598
interface FieldValidators<
599
TParentData,
600
TName extends DeepKeys<TParentData>,
601
TData extends DeepValue<TParentData, TName>,
602
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
603
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
604
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
605
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
606
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
607
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
608
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
609
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>,
610
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData>,
611
> {
612
/** Validator that runs when field is mounted */
613
onMount?: TOnMount;
614
615
/** Validator that runs when field value changes */
616
onChange?: TOnChange;
617
618
/** Async validator that runs when field value changes */
619
onChangeAsync?: TOnChangeAsync;
620
621
/** Validator that runs when field is blurred */
622
onBlur?: TOnBlur;
623
624
/** Async validator that runs when field is blurred */
625
onBlurAsync?: TOnBlurAsync;
626
627
/** Validator that runs on form submission */
628
onSubmit?: TOnSubmit;
629
630
/** Async validator that runs on form submission */
631
onSubmitAsync?: TOnSubmitAsync;
632
633
/** Validator that runs dynamically based on validation logic */
634
onDynamic?: TOnDynamic;
635
636
/** Async validator that runs dynamically based on validation logic */
637
onDynamicAsync?: TOnDynamicAsync;
638
639
/**
640
* Array of field paths to listen to for changes
641
* When any of these fields change, this field's onChange/onChangeAsync validators run
642
*/
643
onChangeListenTo?: DeepKeys<TParentData>[];
644
645
/**
646
* Array of field paths to listen to for blur events
647
* When any of these fields blur, this field's onBlur/onBlurAsync validators run
648
*/
649
onBlurListenTo?: DeepKeys<TParentData>[];
650
}
651
```
652
653
### FieldListeners
654
655
Event listener configuration for field lifecycle events.
656
657
```typescript { .api }
658
interface FieldListeners<
659
TParentData,
660
TName extends DeepKeys<TParentData>,
661
TData extends DeepValue<TParentData, TName>,
662
...
663
> {
664
/**
665
* Listener called when field is mounted
666
* @param fieldApi - Field API instance
667
*/
668
onMount?: (
669
fieldApi: FieldApi<TParentData, TName, TData, ...>,
670
) => void;
671
672
/**
673
* Listener called when field value changes
674
* @param fieldApi - Field API instance
675
*/
676
onChange?: (
677
fieldApi: FieldApi<TParentData, TName, TData, ...>,
678
) => void;
679
680
/**
681
* Listener called when field is blurred
682
* @param fieldApi - Field API instance
683
*/
684
onBlur?: (
685
fieldApi: FieldApi<TParentData, TName, TData, ...>,
686
) => void;
687
688
/**
689
* Listener called on form submission
690
* @param fieldApi - Field API instance
691
*/
692
onSubmit?: (
693
fieldApi: FieldApi<TParentData, TName, TData, ...>,
694
) => void;
695
}
696
```
697
698
### Field Validation Types
699
700
```typescript { .api }
701
/**
702
* Synchronous field validation function
703
* @param props.value - Current field value
704
* @param props.fieldApi - Field API instance
705
* @returns Validation error or undefined if valid
706
*/
707
type FieldValidateFn<TParentData, TName, TData> = (props: {
708
value: TData;
709
fieldApi: FieldApi<TParentData, TName, TData, ...>;
710
}) => ValidationError | Promise<ValidationError>;
711
712
/**
713
* Asynchronous field validation function with abort signal support
714
* @param props.value - Current field value
715
* @param props.signal - AbortSignal for cancellation
716
* @param props.fieldApi - Field API instance
717
* @returns Promise resolving to validation error or undefined if valid
718
*/
719
type FieldValidateAsyncFn<TParentData, TName, TData> = (props: {
720
value: TData;
721
signal: AbortSignal;
722
fieldApi: FieldApi<TParentData, TName, TData, ...>;
723
}) => ValidationError | Promise<ValidationError>;
724
725
/** Union of validation function or Standard Schema validator */
726
type FieldValidateOrFn<TParentData, TName, TData> =
727
| FieldValidateFn<TParentData, TName, TData>
728
| StandardSchemaV1<TData, TData>;
729
730
/** Union of async validation function or Standard Schema validator */
731
type FieldAsyncValidateOrFn<TParentData, TName, TData> =
732
| FieldValidateAsyncFn<TParentData, TName, TData>
733
| StandardSchemaV1<TData, TData>;
734
```
735
736
### Helper Types
737
738
```typescript { .api }
739
/** Type representing any FieldApi instance */
740
type AnyFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
741
742
/** Options for controlling metadata and validation updates */
743
interface UpdateMetaOptions {
744
/** Skip metadata update */
745
dontUpdateMeta?: boolean;
746
747
/** Skip validation after update */
748
dontValidate?: boolean;
749
750
/** Skip running listeners */
751
dontRunListeners?: boolean;
752
}
753
```
754
755
## Helper Types
756
757
Type aliases for convenience when working with fields without needing to specify all generic parameters.
758
759
```typescript { .api }
760
/**
761
* FieldApi with all generics set to any for convenience in dynamic or loosely-typed contexts
762
* Useful when you need to work with fields of unknown structure
763
*/
764
type AnyFieldApi = FieldApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
765
766
/**
767
* FieldMeta with all generics set to any for convenience
768
* Useful for generic field metadata handling
769
*/
770
type AnyFieldMeta = FieldMeta<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
771
```
772
773
## Usage Examples
774
775
### Basic Field with Validation
776
777
```typescript
778
import { useForm } from '@tanstack/react-form';
779
780
function MyForm() {
781
const form = useForm({
782
defaultValues: {
783
email: '',
784
},
785
});
786
787
return (
788
<form.Field
789
name="email"
790
validators={{
791
onChange: ({ value }) => {
792
if (!value.includes('@')) {
793
return 'Invalid email address';
794
}
795
return undefined;
796
},
797
onChangeAsync: async ({ value, signal }) => {
798
// Check email availability with server
799
const response = await fetch(`/api/check-email?email=${value}`, { signal });
800
const data = await response.json();
801
return data.available ? undefined : 'Email already registered';
802
},
803
}}
804
>
805
{(field) => (
806
<div>
807
<label htmlFor={field.name}>Email:</label>
808
<input
809
id={field.name}
810
value={field.state.value}
811
onChange={(e) => field.handleChange(e.target.value)}
812
onBlur={field.handleBlur}
813
/>
814
{field.state.meta.isTouched && field.state.meta.errors.length > 0 && (
815
<em>{field.state.meta.errors[0]}</em>
816
)}
817
{field.state.meta.isValidating && <span>Validating...</span>}
818
</div>
819
)}
820
</form.Field>
821
);
822
}
823
```
824
825
### Array Field with Dynamic Items
826
827
```typescript
828
function TodoListForm() {
829
const form = useForm({
830
defaultValues: {
831
todos: [{ text: '', completed: false }],
832
},
833
});
834
835
return (
836
<form.Field name="todos" mode="array">
837
{(field) => (
838
<div>
839
{field.state.value.map((_, index) => (
840
<form.Field key={index} name={`todos[${index}].text`}>
841
{(subField) => (
842
<div>
843
<input
844
value={subField.state.value}
845
onChange={(e) => subField.handleChange(e.target.value)}
846
/>
847
<button
848
type="button"
849
onClick={() => field.removeValue(index)}
850
>
851
Remove
852
</button>
853
</div>
854
)}
855
</form.Field>
856
))}
857
<button
858
type="button"
859
onClick={() => field.pushValue({ text: '', completed: false })}
860
>
861
Add Todo
862
</button>
863
</div>
864
)}
865
</form.Field>
866
);
867
}
868
```
869
870
### Field with Cross-Field Validation
871
872
```typescript
873
function PasswordForm() {
874
const form = useForm({
875
defaultValues: {
876
password: '',
877
confirmPassword: '',
878
},
879
});
880
881
return (
882
<>
883
<form.Field name="password">
884
{(field) => (
885
<input
886
type="password"
887
value={field.state.value}
888
onChange={(e) => field.handleChange(e.target.value)}
889
/>
890
)}
891
</form.Field>
892
893
<form.Field
894
name="confirmPassword"
895
validators={{
896
onChangeListenTo: ['password'],
897
onChange: ({ value, fieldApi }) => {
898
const password = fieldApi.form.getFieldValue('password');
899
if (value !== password) {
900
return 'Passwords do not match';
901
}
902
return undefined;
903
},
904
}}
905
>
906
{(field) => (
907
<div>
908
<input
909
type="password"
910
value={field.state.value}
911
onChange={(e) => field.handleChange(e.target.value)}
912
/>
913
{field.state.meta.errors[0] && (
914
<em>{field.state.meta.errors[0]}</em>
915
)}
916
</div>
917
)}
918
</form.Field>
919
</>
920
);
921
}
922
```
923
924
### Using useField Hook Directly
925
926
```typescript
927
import { useForm, useField } from '@tanstack/react-form';
928
929
function EmailInput() {
930
const form = useForm({
931
defaultValues: {
932
email: '',
933
},
934
});
935
936
const emailField = useField({
937
form,
938
name: 'email',
939
validators: {
940
onChange: ({ value }) => {
941
if (!value.includes('@')) {
942
return 'Invalid email';
943
}
944
return undefined;
945
},
946
},
947
});
948
949
return (
950
<div>
951
<input
952
value={emailField.state.value}
953
onChange={(e) => emailField.handleChange(e.target.value)}
954
onBlur={emailField.handleBlur}
955
/>
956
{emailField.state.meta.isTouched && emailField.state.meta.errors[0]}
957
</div>
958
);
959
}
960
```
961