0
# React Hooks
1
2
React-specific hooks for form and field state management, field grouping, transformations, and reactive subscriptions. These hooks provide seamless integration with React's lifecycle and state management.
3
4
## Capabilities
5
6
### useForm
7
8
Creates and manages a form instance with React-specific additions.
9
10
```typescript { .api }
11
/**
12
* A custom React Hook that returns an extended instance of the FormApi class
13
* This API encapsulates all necessary functionalities related to the form
14
*
15
* @param opts - Form configuration options
16
* @returns Form API instance extended with React-specific features
17
*/
18
function useForm<
19
TFormData,
20
TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,
21
TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,
22
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
23
TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,
24
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
25
TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,
26
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
27
TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,
28
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
29
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
30
TSubmitMeta = never,
31
>(
32
opts?: FormOptions<
33
TFormData,
34
TOnMount,
35
TOnChange,
36
TOnChangeAsync,
37
TOnBlur,
38
TOnBlurAsync,
39
TOnSubmit,
40
TOnSubmitAsync,
41
TOnDynamic,
42
TOnDynamicAsync,
43
TOnServer,
44
TSubmitMeta
45
>,
46
): ReactFormExtendedApi<
47
TFormData,
48
TOnMount,
49
TOnChange,
50
TOnChangeAsync,
51
TOnBlur,
52
TOnBlurAsync,
53
TOnSubmit,
54
TOnSubmitAsync,
55
TOnDynamic,
56
TOnDynamicAsync,
57
TOnServer,
58
TSubmitMeta
59
>;
60
```
61
62
The returned `ReactFormExtendedApi` includes:
63
- All methods and properties from `FormApi`
64
- `Field`: Pre-bound Field component for this form
65
- `Subscribe`: Component for subscribing to form state changes
66
67
**Usage Example:**
68
69
```typescript
70
import { useForm } from '@tanstack/react-form';
71
72
function MyForm() {
73
const form = useForm({
74
defaultValues: {
75
name: '',
76
age: 0,
77
},
78
onSubmit: async ({ value }) => {
79
await submitToServer(value);
80
},
81
});
82
83
return (
84
<form onSubmit={(e) => {
85
e.preventDefault();
86
form.handleSubmit();
87
}}>
88
<form.Field name="name">
89
{(field) => (
90
<input
91
value={field.state.value}
92
onChange={(e) => field.handleChange(e.target.value)}
93
/>
94
)}
95
</form.Field>
96
97
<form.Subscribe selector={(state) => state.canSubmit}>
98
{(canSubmit) => (
99
<button type="submit" disabled={!canSubmit}>
100
Submit
101
</button>
102
)}
103
</form.Subscribe>
104
</form>
105
);
106
}
107
```
108
109
### useField
110
111
Hook for managing an individual field in a form.
112
113
```typescript { .api }
114
/**
115
* A hook for managing a field in a form
116
*
117
* @param opts - Field configuration options including form reference and field name
118
* @returns FieldApi instance for the specified field
119
*/
120
function useField<
121
TParentData,
122
TName extends DeepKeys<TParentData>,
123
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
124
TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
125
TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
126
TOnChangeAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
127
TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
128
TOnBlurAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
129
TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
130
TOnSubmitAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
131
TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData> = undefined,
132
TOnDynamicAsync extends undefined | FieldAsyncValidateOrFn<TParentData, TName, TData> = undefined,
133
TFormOnMount extends undefined | FormValidateOrFn<TParentData> = undefined,
134
TFormOnChange extends undefined | FormValidateOrFn<TParentData> = undefined,
135
TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
136
TFormOnBlur extends undefined | FormValidateOrFn<TParentData> = undefined,
137
TFormOnBlurAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
138
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData> = undefined,
139
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
140
TFormOnDynamic extends undefined | FormValidateOrFn<TParentData> = undefined,
141
TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
142
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData> = undefined,
143
TParentSubmitMeta = never,
144
>(
145
opts: UseFieldOptions<
146
TParentData,
147
TName,
148
TData,
149
TOnMount,
150
TOnChange,
151
TOnChangeAsync,
152
TOnBlur,
153
TOnBlurAsync,
154
TOnSubmit,
155
TOnSubmitAsync,
156
TOnDynamic,
157
TOnDynamicAsync,
158
TFormOnMount,
159
TFormOnChange,
160
TFormOnChangeAsync,
161
TFormOnBlur,
162
TFormOnBlurAsync,
163
TFormOnSubmit,
164
TFormOnSubmitAsync,
165
TFormOnDynamic,
166
TFormOnDynamicAsync,
167
TFormOnServer,
168
TParentSubmitMeta
169
>,
170
): FieldApi<
171
TParentData,
172
TName,
173
TData,
174
TOnMount,
175
TOnChange,
176
TOnChangeAsync,
177
TOnBlur,
178
TOnBlurAsync,
179
TOnSubmit,
180
TOnSubmitAsync,
181
TOnDynamic,
182
TOnDynamicAsync,
183
TFormOnMount,
184
TFormOnChange,
185
TFormOnChangeAsync,
186
TFormOnBlur,
187
TFormOnBlurAsync,
188
TFormOnSubmit,
189
TFormOnSubmitAsync,
190
TFormOnDynamic,
191
TFormOnDynamicAsync,
192
TFormOnServer,
193
TParentSubmitMeta
194
>;
195
```
196
197
**Usage Example:**
198
199
```typescript
200
import { useForm, useField } from '@tanstack/react-form';
201
202
function EmailField() {
203
const form = useForm({
204
defaultValues: {
205
email: '',
206
},
207
});
208
209
const field = useField({
210
form,
211
name: 'email',
212
validators: {
213
onChange: ({ value }) =>
214
!value.includes('@') ? 'Invalid email' : undefined,
215
},
216
});
217
218
return (
219
<div>
220
<input
221
value={field.state.value}
222
onChange={(e) => field.handleChange(e.target.value)}
223
onBlur={field.handleBlur}
224
/>
225
{field.state.meta.errors[0] && (
226
<span>{field.state.meta.errors[0]}</span>
227
)}
228
</div>
229
);
230
}
231
```
232
233
### useFieldGroup
234
235
Hook for managing groups of related fields with shared state.
236
237
```typescript { .api }
238
/**
239
* Hook for managing a group of related fields
240
* Field groups allow you to work with a subset of form data as a logical unit
241
*
242
* @param opts - Field group configuration
243
* @returns Field group API with Field, Subscribe, and field manipulation methods
244
*/
245
function useFieldGroup<
246
TFormData,
247
TFieldGroupData,
248
TFields extends
249
| DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>
250
| FieldsMap<TFormData, TFieldGroupData>,
251
TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,
252
TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,
253
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
254
TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,
255
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
256
TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,
257
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
258
TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,
259
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
260
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,
261
TComponents extends Record<string, ComponentType<any>> = {},
262
TFormComponents extends Record<string, ComponentType<any>> = {},
263
TSubmitMeta = never,
264
>(opts: {
265
/** Parent form or field group instance */
266
form:
267
| AppFieldExtendedReactFormApi<
268
TFormData,
269
TOnMount,
270
TOnChange,
271
TOnChangeAsync,
272
TOnBlur,
273
TOnBlurAsync,
274
TOnSubmit,
275
TOnSubmitAsync,
276
TOnDynamic,
277
TOnDynamicAsync,
278
TOnServer,
279
TSubmitMeta,
280
TComponents,
281
TFormComponents
282
>
283
| AppFieldExtendedReactFieldGroupApi<...>;
284
285
/**
286
* Field definitions - can be:
287
* - A field path string (e.g., "user.address")
288
* - A field map object mapping field group keys to form field paths
289
*/
290
fields: TFields;
291
292
/** Default values for the field group */
293
defaultValues?: TFieldGroupData;
294
295
/** Submit metadata for the field group */
296
onSubmitMeta?: TSubmitMeta;
297
298
/** Form-level components to inject */
299
formComponents: TFormComponents;
300
}): AppFieldExtendedReactFieldGroupApi<
301
TFormData,
302
TFieldGroupData,
303
TFields,
304
TOnMount,
305
TOnChange,
306
TOnChangeAsync,
307
TOnBlur,
308
TOnBlurAsync,
309
TOnSubmit,
310
TOnSubmitAsync,
311
TOnDynamic,
312
TOnDynamicAsync,
313
TOnServer,
314
TSubmitMeta,
315
TComponents,
316
TFormComponents
317
>;
318
```
319
320
The returned API includes:
321
- All methods from `FieldGroupApi`
322
- `Field`: Component for rendering fields within the group
323
- `AppField`: Component with custom field components
324
- `AppForm`: Component with custom form components
325
- `Subscribe`: Component for subscribing to field group state
326
327
**Usage Example:**
328
329
```typescript
330
import { useForm, useFieldGroup } from '@tanstack/react-form';
331
332
function AddressForm() {
333
const form = useForm({
334
defaultValues: {
335
user: {
336
name: '',
337
address: {
338
street: '',
339
city: '',
340
zip: '',
341
},
342
},
343
},
344
});
345
346
const addressGroup = useFieldGroup({
347
form,
348
fields: 'user.address',
349
formComponents: {},
350
});
351
352
return (
353
<div>
354
<addressGroup.Field name="street">
355
{(field) => (
356
<input
357
value={field.state.value}
358
onChange={(e) => field.handleChange(e.target.value)}
359
/>
360
)}
361
</addressGroup.Field>
362
363
<addressGroup.Field name="city">
364
{(field) => (
365
<input
366
value={field.state.value}
367
onChange={(e) => field.handleChange(e.target.value)}
368
/>
369
)}
370
</addressGroup.Field>
371
372
<addressGroup.Subscribe>
373
{(state) => <pre>{JSON.stringify(state.values, null, 2)}</pre>}
374
</addressGroup.Subscribe>
375
</div>
376
);
377
}
378
```
379
380
### useStore
381
382
Hook for subscribing to store updates with optional selector.
383
384
```typescript { .api }
385
/**
386
* Hook for subscribing to store updates from form and field APIs
387
* Efficiently re-renders only when selected state changes
388
*
389
* @param store - Store instance from FormApi or FieldApi
390
* @param selector - Optional function to select specific state slice
391
* @returns Selected state value
392
*/
393
function useStore<TState, TSelected = TState>(
394
store: Store<TState>,
395
selector?: (state: TState) => TSelected,
396
): TSelected;
397
```
398
399
**Usage Example:**
400
401
```typescript
402
import { useForm, useStore } from '@tanstack/react-form';
403
404
function FormStatus() {
405
const form = useForm({ /* ... */ });
406
407
// Subscribe to specific state slice
408
const canSubmit = useStore(form.store, (state) => state.canSubmit);
409
const isSubmitting = useStore(form.store, (state) => state.isSubmitting);
410
411
return (
412
<div>
413
<button type="submit" disabled={!canSubmit || isSubmitting}>
414
{isSubmitting ? 'Submitting...' : 'Submit'}
415
</button>
416
</div>
417
);
418
}
419
```
420
421
### useTransform
422
423
Hook for creating form transformations that apply based on dependencies.
424
425
```typescript { .api }
426
/**
427
* Creates a form transformation that can be applied to modify form behavior
428
* Transformations run when dependencies change
429
*
430
* @param fn - Transformation function that receives base form and returns modified form
431
* @param deps - Array of dependencies that trigger transformation re-execution
432
* @returns FormTransform object
433
*/
434
function useTransform(
435
fn: (formBase: AnyFormApi) => AnyFormApi,
436
deps: unknown[],
437
): FormTransform<any, any, any, any, any, any, any, any, any, any, any, any>;
438
```
439
440
**Usage Example:**
441
442
```typescript
443
import { useForm, useTransform } from '@tanstack/react-form';
444
445
function DynamicForm({ mode }) {
446
const transform = useTransform(
447
(form) => {
448
if (mode === 'readonly') {
449
// Override form methods to prevent edits
450
return {
451
...form,
452
setFieldValue: () => {},
453
handleSubmit: () => Promise.resolve(),
454
};
455
}
456
return form;
457
},
458
[mode]
459
);
460
461
const form = useForm({
462
defaultValues: { name: '' },
463
transform,
464
});
465
466
return <form.Field name="name">{/* ... */}</form.Field>;
467
}
468
```
469
470
## Types
471
472
### FieldGroupApi Types
473
474
```typescript { .api }
475
class FieldGroupApi<
476
TFormData,
477
TFieldGroupData,
478
TFields extends
479
| DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>
480
| FieldsMap<TFormData, TFieldGroupData>,
481
TOnMount extends undefined | FormValidateOrFn<TFormData>,
482
TOnChange extends undefined | FormValidateOrFn<TFormData>,
483
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
484
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
485
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
486
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
487
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
488
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
489
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
490
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
491
TSubmitMeta,
492
> {
493
/** Store instance for reactive state */
494
store: Store<FieldGroupState<TFieldGroupData>>;
495
496
/** Current field group state */
497
state: FieldGroupState<TFieldGroupData>;
498
499
/** Parent form or field group */
500
form: FormApi<TFormData, ...> | FieldGroupApi<...>;
501
502
/** Field group configuration */
503
options: FieldGroupOptions<TFormData, TFieldGroupData, TFields, ...>;
504
505
constructor(opts: FieldGroupOptions<TFormData, TFieldGroupData, TFields, ...>);
506
507
/**
508
* Mounts the field group
509
* @returns Cleanup function to unmount
510
*/
511
mount(): () => void;
512
513
/**
514
* Gets field options for a specific field in the group
515
* @param props - Field properties including name
516
* @returns Field options for use with Field component
517
*/
518
getFormFieldOptions<TName extends DeepKeys<TFieldGroupData>>(
519
props: { name: TName },
520
): FieldApiOptions<...>;
521
522
/**
523
* Gets the value of a field in the group
524
* @param key - Field key
525
* @returns Field value
526
*/
527
getFieldValue<TKey extends keyof TFieldGroupData>(
528
key: TKey,
529
): TFieldGroupData[TKey];
530
531
/**
532
* Sets the value of a field in the group
533
* @param key - Field key
534
* @param updater - Value or function to update value
535
* @param opts - Update options
536
*/
537
setFieldValue<TKey extends keyof TFieldGroupData>(
538
key: TKey,
539
updater: Updater<TFieldGroupData[TKey]>,
540
opts?: UpdateMetaOptions,
541
): void;
542
}
543
544
interface FieldGroupOptions<
545
TFormData,
546
TFieldGroupData,
547
TFields extends
548
| DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>
549
| FieldsMap<TFormData, TFieldGroupData>,
550
TOnMount extends undefined | FormValidateOrFn<TFormData>,
551
TOnChange extends undefined | FormValidateOrFn<TFormData>,
552
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
553
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
554
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
555
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
556
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
557
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
558
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
559
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
560
TSubmitMeta,
561
> {
562
/** Parent form or field group instance */
563
form: FormApi<TFormData, ...> | FieldGroupApi<...>;
564
565
/** Field path or field mapping */
566
fields: TFields;
567
568
/** Default values for the field group */
569
defaultValues?: TFieldGroupData;
570
571
/** Submit metadata */
572
onSubmitMeta?: TSubmitMeta;
573
}
574
575
interface FieldGroupState<TFieldGroupData> {
576
/** Current field group values */
577
values: TFieldGroupData;
578
}
579
580
/** Type representing any FieldGroupApi instance */
581
type AnyFieldGroupApi = FieldGroupApi<any, any, any, any, any, any, any, any, any, any, any, any, any>;
582
```
583
584
### FormTransform Type
585
586
```typescript { .api }
587
interface FormTransform<
588
TFormData,
589
TOnMount extends undefined | FormValidateOrFn<TFormData>,
590
TOnChange extends undefined | FormValidateOrFn<TFormData>,
591
TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
592
TOnBlur extends undefined | FormValidateOrFn<TFormData>,
593
TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
594
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
595
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
596
TOnDynamic extends undefined | FormValidateOrFn<TFormData>,
597
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
598
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
599
TSubmitMeta,
600
> {
601
/** Transformation function */
602
fn: (
603
formBase: FormApi<TFormData, ...>,
604
) => FormApi<TFormData, ...>;
605
606
/** Dependencies that trigger re-execution */
607
deps: unknown[];
608
}
609
```
610
611
### Helper Types
612
613
Type aliases for convenience when working with field groups without needing to specify all generic parameters.
614
615
```typescript { .api }
616
/**
617
* FieldGroupApi with all generics set to any for convenience in dynamic or loosely-typed contexts
618
* Useful when you need to work with field groups of unknown structure
619
*/
620
type AnyFieldGroupApi = FieldGroupApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
621
```
622
623
## Usage Examples
624
625
### Advanced Field Group with Mapping
626
627
```typescript
628
function ProfileForm() {
629
const form = useForm({
630
defaultValues: {
631
firstName: '',
632
lastName: '',
633
email: '',
634
},
635
});
636
637
// Map flat form structure to grouped structure
638
const nameGroup = useFieldGroup({
639
form,
640
fields: {
641
first: 'firstName',
642
last: 'lastName',
643
},
644
formComponents: {},
645
});
646
647
return (
648
<div>
649
<h2>Name</h2>
650
<nameGroup.Field name="first">
651
{(field) => (
652
<input
653
value={field.state.value}
654
onChange={(e) => field.handleChange(e.target.value)}
655
/>
656
)}
657
</nameGroup.Field>
658
659
<nameGroup.Field name="last">
660
{(field) => (
661
<input
662
value={field.state.value}
663
onChange={(e) => field.handleChange(e.target.value)}
664
/>
665
)}
666
</nameGroup.Field>
667
</div>
668
);
669
}
670
```
671
672
### Conditional Rendering with useStore
673
674
```typescript
675
function ConditionalFields() {
676
const form = useForm({
677
defaultValues: {
678
hasAddress: false,
679
address: {
680
street: '',
681
city: '',
682
},
683
},
684
});
685
686
const hasAddress = useStore(
687
form.store,
688
(state) => state.values.hasAddress
689
);
690
691
return (
692
<div>
693
<form.Field name="hasAddress">
694
{(field) => (
695
<label>
696
<input
697
type="checkbox"
698
checked={field.state.value}
699
onChange={(e) => field.handleChange(e.target.checked)}
700
/>
701
Has Address
702
</label>
703
)}
704
</form.Field>
705
706
{hasAddress && (
707
<>
708
<form.Field name="address.street">
709
{(field) => (
710
<input
711
value={field.state.value}
712
onChange={(e) => field.handleChange(e.target.value)}
713
/>
714
)}
715
</form.Field>
716
717
<form.Field name="address.city">
718
{(field) => (
719
<input
720
value={field.state.value}
721
onChange={(e) => field.handleChange(e.target.value)}
722
/>
723
)}
724
</form.Field>
725
</>
726
)}
727
</div>
728
);
729
}
730
```
731