or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced.mdfield-api.mdform-api.mdframework-integrations.mdhooks.mdindex.mdvalidation.md

hooks.mddocs/

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