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

advanced.mddocs/

0

# Advanced Form Patterns

1

2

Advanced features for creating custom form hooks with component injection, React contexts for form and field data, higher-order components, and utility functions for form manipulation and helper operations.

3

4

## Capabilities

5

6

### createFormHook

7

8

Creates a custom form hook with injected field and form components.

9

10

```typescript { .api }

11

/**

12

* Creates a custom form hook with extended field and form components

13

* Enables component injection pattern for reusable form UI components

14

*

15

* @param props.fieldComponents - Custom field-level components to inject

16

* @param props.fieldContext - React context for field data

17

* @param props.formContext - React context for form data

18

* @param props.formComponents - Custom form-level components to inject

19

* @returns Object containing useAppForm, withForm, and withFieldGroup functions

20

*/

21

function createFormHook<

22

const TComponents extends Record<string, ComponentType<any>>,

23

const TFormComponents extends Record<string, ComponentType<any>>,

24

>({

25

fieldComponents,

26

fieldContext,

27

formContext,

28

formComponents,

29

}: CreateFormHookProps<TComponents, TFormComponents>): {

30

/**

31

* Custom form hook with injected components

32

* Similar to useForm but returns AppFieldExtendedReactFormApi with custom components

33

*/

34

useAppForm: <

35

TFormData,

36

TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,

37

TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,

38

TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

39

TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,

40

TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

41

TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,

42

TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

43

TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,

44

TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

45

TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

46

TSubmitMeta = never,

47

>(

48

props: FormOptions<

49

TFormData,

50

TOnMount,

51

TOnChange,

52

TOnChangeAsync,

53

TOnBlur,

54

TOnBlurAsync,

55

TOnSubmit,

56

TOnSubmitAsync,

57

TOnDynamic,

58

TOnDynamicAsync,

59

TOnServer,

60

TSubmitMeta

61

>,

62

) => AppFieldExtendedReactFormApi<

63

TFormData,

64

TOnMount,

65

TOnChange,

66

TOnChangeAsync,

67

TOnBlur,

68

TOnBlurAsync,

69

TOnSubmit,

70

TOnSubmitAsync,

71

TOnDynamic,

72

TOnDynamicAsync,

73

TOnServer,

74

TSubmitMeta,

75

TComponents,

76

TFormComponents

77

>;

78

79

/**

80

* Higher-order component for wrapping forms

81

* Provides form instance to render function

82

*/

83

withForm: <

84

TFormData,

85

TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,

86

TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,

87

TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

88

TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,

89

TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

90

TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,

91

TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

92

TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,

93

TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

94

TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

95

TSubmitMeta = never,

96

TRenderProps extends object = {},

97

>(

98

props: WithFormProps<

99

TFormData,

100

TOnMount,

101

TOnChange,

102

TOnChangeAsync,

103

TOnBlur,

104

TOnBlurAsync,

105

TOnSubmit,

106

TOnSubmitAsync,

107

TOnDynamic,

108

TOnDynamicAsync,

109

TOnServer,

110

TSubmitMeta,

111

TComponents,

112

TFormComponents,

113

TRenderProps

114

>,

115

) => (props: PropsWithChildren<TRenderProps>) => JSX.Element;

116

117

/**

118

* Higher-order component for wrapping field groups

119

* Provides field group instance to render function

120

*/

121

withFieldGroup: <

122

TFieldGroupData,

123

TSubmitMeta = never,

124

TRenderProps extends Record<string, unknown> = {},

125

>(

126

props: WithFieldGroupProps<

127

TFieldGroupData,

128

TComponents,

129

TFormComponents,

130

TSubmitMeta,

131

TRenderProps

132

>,

133

) => <

134

TFormData,

135

TFields extends

136

| DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>

137

| FieldsMap<TFormData, TFieldGroupData>,

138

TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined,

139

TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined,

140

TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

141

TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined,

142

TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

143

TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined,

144

TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

145

TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined,

146

TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

147

TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined,

148

>(

149

params: PropsWithChildren<

150

TRenderProps & {

151

form: AppFieldExtendedReactFormApi<...>;

152

fields: TFields;

153

}

154

>,

155

) => JSX.Element;

156

};

157

158

interface CreateFormHookProps<

159

TFieldComponents extends Record<string, ComponentType<any>>,

160

TFormComponents extends Record<string, ComponentType<any>>,

161

> {

162

/** Custom field-level components (e.g., custom input wrappers) */

163

fieldComponents: TFieldComponents;

164

165

/** React context for accessing field data */

166

fieldContext: Context<AnyFieldApi>;

167

168

/** Custom form-level components (e.g., custom form wrapper) */

169

formComponents: TFormComponents;

170

171

/** React context for accessing form data */

172

formContext: Context<AnyFormApi>;

173

}

174

```

175

176

### createFormHookContexts

177

178

Creates React contexts and hooks for accessing form and field data.

179

180

```typescript { .api }

181

/**

182

* Creates React contexts and hooks for form and field data access

183

* Use this to create contexts before calling createFormHook

184

*

185

* @returns Object containing contexts and hook functions

186

*/

187

function createFormHookContexts(): {

188

/** React context for field data */

189

fieldContext: Context<AnyFieldApi>;

190

191

/**

192

* Hook to access field context within a field component

193

* @returns Current field API instance

194

* @throws Error if called outside of field component context

195

*/

196

useFieldContext: <TData>() => FieldApi<

197

any,

198

string,

199

TData,

200

any,

201

any,

202

any,

203

any,

204

any,

205

any,

206

any,

207

any,

208

any,

209

any,

210

any,

211

any,

212

any,

213

any,

214

any,

215

any,

216

any,

217

any,

218

any,

219

any

220

>;

221

222

/**

223

* Hook to access form context within a form component

224

* @returns Current form API instance

225

* @throws Error if called outside of form component context

226

*/

227

useFormContext: () => ReactFormExtendedApi<

228

Record<string, never>,

229

any,

230

any,

231

any,

232

any,

233

any,

234

any,

235

any,

236

any,

237

any,

238

any,

239

any

240

>;

241

242

/** React context for form data */

243

formContext: Context<AnyFormApi>;

244

};

245

```

246

247

### WithFormProps

248

249

Props interface for withForm higher-order component.

250

251

```typescript { .api }

252

interface WithFormProps<

253

TFormData,

254

TOnMount extends undefined | FormValidateOrFn<TFormData>,

255

TOnChange extends undefined | FormValidateOrFn<TFormData>,

256

TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

257

TOnBlur extends undefined | FormValidateOrFn<TFormData>,

258

TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

259

TOnSubmit extends undefined | FormValidateOrFn<TFormData>,

260

TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

261

TOnDynamic extends undefined | FormValidateOrFn<TFormData>,

262

TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

263

TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,

264

TSubmitMeta,

265

TFieldComponents extends Record<string, ComponentType<any>>,

266

TFormComponents extends Record<string, ComponentType<any>>,

267

TRenderProps extends object = Record<string, never>,

268

> extends FormOptions<

269

TFormData,

270

TOnMount,

271

TOnChange,

272

TOnChangeAsync,

273

TOnBlur,

274

TOnBlurAsync,

275

TOnSubmit,

276

TOnSubmitAsync,

277

TOnDynamic,

278

TOnDynamicAsync,

279

TOnServer,

280

TSubmitMeta

281

> {

282

/** Optional additional props for render function */

283

props?: TRenderProps;

284

285

/**

286

* Render function that receives form instance and props

287

* @param props - Combined props with form API

288

* @returns JSX element

289

*/

290

render: (

291

props: PropsWithChildren<

292

TRenderProps & {

293

form: AppFieldExtendedReactFormApi<

294

TFormData,

295

TOnMount,

296

TOnChange,

297

TOnChangeAsync,

298

TOnBlur,

299

TOnBlurAsync,

300

TOnSubmit,

301

TOnSubmitAsync,

302

TOnDynamic,

303

TOnDynamicAsync,

304

TOnServer,

305

TSubmitMeta,

306

TFieldComponents,

307

TFormComponents

308

>;

309

}

310

>,

311

) => JSX.Element;

312

}

313

```

314

315

### WithFieldGroupProps

316

317

Props interface for withFieldGroup higher-order component.

318

319

```typescript { .api }

320

interface WithFieldGroupProps<

321

TFieldGroupData,

322

TFieldComponents extends Record<string, ComponentType<any>>,

323

TFormComponents extends Record<string, ComponentType<any>>,

324

TSubmitMeta,

325

TRenderProps extends Record<string, unknown> = Record<string, never>,

326

> extends BaseFormOptions<TFieldGroupData, TSubmitMeta> {

327

/** Optional additional props for render function */

328

props?: TRenderProps;

329

330

/**

331

* Render function that receives field group instance and props

332

* @param props - Combined props with field group API

333

* @returns JSX element

334

*/

335

render: (

336

props: PropsWithChildren<

337

TRenderProps & {

338

group: AppFieldExtendedReactFieldGroupApi<

339

unknown,

340

TFieldGroupData,

341

string | FieldsMap<unknown, TFieldGroupData>,

342

undefined | FormValidateOrFn<unknown>,

343

undefined | FormValidateOrFn<unknown>,

344

undefined | FormAsyncValidateOrFn<unknown>,

345

undefined | FormValidateOrFn<unknown>,

346

undefined | FormAsyncValidateOrFn<unknown>,

347

undefined | FormValidateOrFn<unknown>,

348

undefined | FormAsyncValidateOrFn<unknown>,

349

undefined | FormValidateOrFn<unknown>,

350

undefined | FormAsyncValidateOrFn<unknown>,

351

undefined | FormAsyncValidateOrFn<unknown>,

352

unknown extends TSubmitMeta ? never : TSubmitMeta,

353

TFieldComponents,

354

TFormComponents

355

>;

356

}

357

>,

358

) => JSX.Element;

359

}

360

```

361

362

### AppFieldExtendedReactFormApi

363

364

Extended form API with custom components.

365

366

```typescript { .api }

367

type AppFieldExtendedReactFormApi<

368

TFormData,

369

TOnMount extends undefined | FormValidateOrFn<TFormData>,

370

TOnChange extends undefined | FormValidateOrFn<TFormData>,

371

TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

372

TOnBlur extends undefined | FormValidateOrFn<TFormData>,

373

TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

374

TOnSubmit extends undefined | FormValidateOrFn<TFormData>,

375

TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

376

TOnDynamic extends undefined | FormValidateOrFn<TFormData>,

377

TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,

378

TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,

379

TSubmitMeta,

380

TFieldComponents extends Record<string, ComponentType<any>>,

381

TFormComponents extends Record<string, ComponentType<any>>,

382

> = ReactFormExtendedApi<

383

TFormData,

384

TOnMount,

385

TOnChange,

386

TOnChangeAsync,

387

TOnBlur,

388

TOnBlurAsync,

389

TOnSubmit,

390

TOnSubmitAsync,

391

TOnDynamic,

392

TOnDynamicAsync,

393

TOnServer,

394

TSubmitMeta

395

> &

396

TFormComponents & {

397

/** Field component with custom field components injected */

398

AppField: FieldComponent<

399

TFormData,

400

TOnMount,

401

TOnChange,

402

TOnChangeAsync,

403

TOnBlur,

404

TOnBlurAsync,

405

TOnSubmit,

406

TOnSubmitAsync,

407

TOnDynamic,

408

TOnDynamicAsync,

409

TOnServer,

410

TSubmitMeta,

411

TFieldComponents

412

>;

413

414

/** Form wrapper component with context provider */

415

AppForm: ComponentType<PropsWithChildren>;

416

};

417

```

418

419

## Utility Functions

420

421

### Form State Manipulation

422

423

```typescript { .api }

424

/**

425

* Applies an updater function or value to an input

426

* @param updater - Function or value to apply

427

* @param input - Input value

428

* @returns Updated value

429

*/

430

function functionalUpdate<TInput, TOutput = TInput>(

431

updater: Updater<TInput, TOutput>,

432

input: TInput,

433

): TOutput;

434

435

/**

436

* Merges partial state into a form instance

437

* @param baseForm - Base form API instance

438

* @param state - Partial state to merge

439

* @returns Form API with merged state

440

*/

441

function mergeForm<TFormData>(

442

baseForm: FormApi<TFormData, ...>,

443

state: Partial<FormApi<TFormData, ...>['state']>,

444

): FormApi<TFormData, ...>;

445

446

/**

447

* Deep merges source object into target object (mutating)

448

* @param target - Target object to merge into

449

* @param source - Source object to merge from

450

* @returns Merged object

451

*/

452

function mutateMergeDeep(

453

target: object | null | undefined,

454

source: object | null | undefined,

455

): object;

456

```

457

458

### Path Utilities

459

460

```typescript { .api }

461

/**

462

* Gets a value from an object using a path

463

* Supports dot notation and array indices

464

* @param obj - Object to traverse

465

* @param path - Path string or array

466

* @returns Value at path

467

*/

468

function getBy(obj: any, path: any): any;

469

470

/**

471

* Sets a value on an object using a path

472

* Supports dot notation and array indices

473

* @param obj - Object to update

474

* @param path - Path string or array

475

* @param updater - Value or function to set

476

* @returns Updated object

477

*/

478

function setBy(obj: any, path: any, updater: Updater<any>): any;

479

480

/**

481

* Deletes a field on an object using a path

482

* @param obj - Object to update

483

* @param path - Path to delete

484

* @returns Updated object

485

*/

486

function deleteBy(obj: any, path: any): any;

487

488

/**

489

* Converts a path string to an array of path segments

490

* @param str - Path string or array

491

* @returns Array of path segments

492

*/

493

function makePathArray(str: string | Array<string | number>): Array<string | number>;

494

495

/**

496

* Concatenates two paths together

497

* @param path1 - First path

498

* @param path2 - Second path

499

* @returns Concatenated path string

500

*/

501

function concatenatePaths(path1: string, path2: string): string;

502

```

503

504

### Helper Functions

505

506

```typescript { .api }

507

/**

508

* Creates type-safe form options with proper inference

509

* @param defaultOpts - Form options with custom properties

510

* @returns Type-safe form options

511

*/

512

function formOptions<TOptions, TFormData, ...>(

513

defaultOpts: Partial<FormOptions<TFormData, ...>> & TOptions,

514

): TOptions;

515

516

/**

517

* Creates a map of field keys to their names

518

* @param values - Object with field values

519

* @returns Map of keys to names

520

*/

521

function createFieldMap<T>(values: Readonly<T>): { [K in keyof T]: K };

522

523

/**

524

* Checks if a value is a non-empty array

525

* @param obj - Value to check

526

* @returns True if value is non-empty array

527

*/

528

function isNonEmptyArray(obj: any): boolean;

529

530

/**

531

* Generates a unique identifier

532

* @returns Unique ID string

533

*/

534

function uuid(): string;

535

536

/**

537

* Merges default options with provided options

538

* @param defaultOpts - Default options

539

* @param opts - Override options

540

* @returns Merged options

541

*/

542

function mergeOpts<T>(defaultOpts: T, opts?: T): T;

543

544

/**

545

* Deep equality check for two objects

546

* @param objA - First object

547

* @param objB - Second object

548

* @returns True if objects are deeply equal

549

*/

550

function evaluate<T>(objA: T, objB: T): boolean;

551

```

552

553

### Field Metadata Helpers

554

555

```typescript { .api }

556

/**

557

* Default field metadata object

558

*/

559

const defaultFieldMeta: AnyFieldMeta;

560

561

/**

562

* Helper function for managing field metadata during array operations

563

* @param formApi - The form API instance

564

* @returns Object with handleArrayFieldMetaShift method for managing field metadata

565

*/

566

function metaHelper<TFormData, ...>(

567

formApi: FormApi<TFormData, ...>

568

): {

569

handleArrayFieldMetaShift: (

570

field: DeepKeys<TFormData>,

571

index: number,

572

mode: 'insert' | 'remove' | 'swap' | 'move',

573

secondIndex?: number

574

) => void;

575

};

576

```

577

578

## Usage Examples

579

580

### Creating a Custom Form Hook with Component Library

581

582

```typescript

583

import { createFormHook, createFormHookContexts } from '@tanstack/react-form';

584

585

// Create contexts

586

const contexts = createFormHookContexts();

587

588

// Define custom components

589

const fieldComponents = {

590

// Custom input component with built-in styling

591

Input: ({ ...props }) => (

592

<input

593

className="custom-input"

594

{...props}

595

/>

596

),

597

598

// Custom select component

599

Select: ({ options, ...props }) => (

600

<select className="custom-select" {...props}>

601

{options.map((opt) => (

602

<option key={opt.value} value={opt.value}>

603

{opt.label}

604

</option>

605

))}

606

</select>

607

),

608

};

609

610

const formComponents = {

611

// Custom form wrapper

612

Card: ({ children }) => (

613

<div className="card">

614

<div className="card-body">{children}</div>

615

</div>

616

),

617

};

618

619

// Create custom hook

620

const { useAppForm, withForm, withFieldGroup } = createFormHook({

621

fieldComponents,

622

fieldContext: contexts.fieldContext,

623

formComponents,

624

formContext: contexts.formContext,

625

});

626

627

// Use the custom form hook

628

function MyForm() {

629

const form = useAppForm({

630

defaultValues: {

631

name: '',

632

color: 'red',

633

},

634

});

635

636

return (

637

<form.AppForm>

638

<form.Card>

639

<form.AppField name="name">

640

{(field) => (

641

<div>

642

<label>Name:</label>

643

<field.Input

644

value={field.state.value}

645

onChange={(e) => field.handleChange(e.target.value)}

646

/>

647

</div>

648

)}

649

</form.AppField>

650

651

<form.AppField name="color">

652

{(field) => (

653

<div>

654

<label>Color:</label>

655

<field.Select

656

options={[

657

{ value: 'red', label: 'Red' },

658

{ value: 'blue', label: 'Blue' },

659

]}

660

value={field.state.value}

661

onChange={(e) => field.handleChange(e.target.value)}

662

/>

663

</div>

664

)}

665

</form.AppField>

666

</form.Card>

667

</form.AppForm>

668

);

669

}

670

```

671

672

### Using withForm Higher-Order Component

673

674

```typescript

675

const { withForm } = createFormHook({

676

fieldComponents: {},

677

fieldContext: contexts.fieldContext,

678

formComponents: {},

679

formContext: contexts.formContext,

680

});

681

682

const ContactForm = withForm({

683

defaultValues: {

684

name: '',

685

email: '',

686

},

687

validators: {

688

onChange: ({ value }) => {

689

if (!value.email.includes('@')) {

690

return 'Invalid email';

691

}

692

return undefined;

693

},

694

},

695

onSubmit: async ({ value }) => {

696

await submitToServer(value);

697

},

698

render: ({ form }) => (

699

<form.AppForm>

700

<form.AppField name="name">

701

{(field) => (

702

<input

703

value={field.state.value}

704

onChange={(e) => field.handleChange(e.target.value)}

705

/>

706

)}

707

</form.AppField>

708

709

<form.AppField name="email">

710

{(field) => (

711

<input

712

value={field.state.value}

713

onChange={(e) => field.handleChange(e.target.value)}

714

/>

715

)}

716

</form.AppField>

717

718

<button type="submit">Submit</button>

719

</form.AppForm>

720

),

721

});

722

723

// Use the component

724

function App() {

725

return <ContactForm />;

726

}

727

```

728

729

### Using withFieldGroup for Reusable Address Input

730

731

```typescript

732

const { withFieldGroup } = createFormHook({

733

fieldComponents: {},

734

fieldContext: contexts.fieldContext,

735

formComponents: {},

736

formContext: contexts.formContext,

737

});

738

739

const AddressInput = withFieldGroup({

740

defaultValues: {

741

street: '',

742

city: '',

743

state: '',

744

zip: '',

745

},

746

render: ({ group }) => (

747

<div className="address-group">

748

<group.Field name="street">

749

{(field) => (

750

<input

751

placeholder="Street"

752

value={field.state.value}

753

onChange={(e) => field.handleChange(e.target.value)}

754

/>

755

)}

756

</group.Field>

757

758

<group.Field name="city">

759

{(field) => (

760

<input

761

placeholder="City"

762

value={field.state.value}

763

onChange={(e) => field.handleChange(e.target.value)}

764

/>

765

)}

766

</group.Field>

767

768

<group.Field name="state">

769

{(field) => (

770

<input

771

placeholder="State"

772

value={field.state.value}

773

onChange={(e) => field.handleChange(e.target.value)}

774

/>

775

)}

776

</group.Field>

777

778

<group.Field name="zip">

779

{(field) => (

780

<input

781

placeholder="ZIP"

782

value={field.state.value}

783

onChange={(e) => field.handleChange(e.target.value)}

784

/>

785

)}

786

</group.Field>

787

</div>

788

),

789

});

790

791

// Use in a form

792

function UserForm() {

793

const form = useAppForm({

794

defaultValues: {

795

name: '',

796

shippingAddress: {

797

street: '',

798

city: '',

799

state: '',

800

zip: '',

801

},

802

billingAddress: {

803

street: '',

804

city: '',

805

state: '',

806

zip: '',

807

},

808

},

809

});

810

811

return (

812

<form.AppForm>

813

<h2>Shipping Address</h2>

814

<AddressInput form={form} fields="shippingAddress" />

815

816

<h2>Billing Address</h2>

817

<AddressInput form={form} fields="billingAddress" />

818

</form.AppForm>

819

);

820

}

821

```

822

823

### Using Context Hooks

824

825

```typescript

826

import { createFormHookContexts } from '@tanstack/react-form';

827

828

const { useFieldContext, useFormContext } = createFormHookContexts();

829

830

// Custom field wrapper component

831

function FieldWrapper({ children }) {

832

const field = useFieldContext<string>();

833

834

return (

835

<div className="field-wrapper">

836

<div className="field-content">{children}</div>

837

{field.state.meta.errors[0] && (

838

<div className="field-error">{field.state.meta.errors[0]}</div>

839

)}

840

</div>

841

);

842

}

843

844

// Custom submit button component

845

function SubmitButton() {

846

const form = useFormContext();

847

848

return (

849

<button

850

type="submit"

851

disabled={!form.state.canSubmit || form.state.isSubmitting}

852

>

853

{form.state.isSubmitting ? 'Submitting...' : 'Submit'}

854

</button>

855

);

856

}

857

```

858

859

### Path Utilities for Dynamic Field Names

860

861

```typescript

862

import { getBy, setBy, concatenatePaths } from '@tanstack/react-form';

863

864

function DynamicFieldName() {

865

const form = useForm({

866

defaultValues: {

867

user: {

868

profile: {

869

firstName: 'John',

870

},

871

},

872

},

873

});

874

875

const basePath = 'user.profile';

876

const fieldName = 'firstName';

877

const fullPath = concatenatePaths(basePath, fieldName);

878

879

// Get value using path

880

const value = getBy(form.state.values, fullPath);

881

console.log(value); // 'John'

882

883

// Set value using path

884

const updated = setBy(

885

form.state.values,

886

fullPath,

887

(current) => current.toUpperCase()

888

);

889

890

return <div>{value}</div>;

891

}

892

```

893

894

## DevTools Integration

895

896

### Event Client

897

898

Event client for integrating with TanStack Form DevTools for debugging and monitoring form state.

899

900

```typescript { .api }

901

/**

902

* Event client instance for form devtools integration

903

* Used internally by FormApi to broadcast form state changes

904

*/

905

const formEventClient: FormEventClient;

906

907

/**

908

* Form state change broadcast event payload

909

*/

910

type BroadcastFormState = {

911

/** Unique form identifier */

912

id: string;

913

/** Current form state */

914

state: AnyFormState;

915

/** Form options */

916

options: AnyFormOptions;

917

};

918

919

/**

920

* Form submission state change broadcast event payload

921

*/

922

type BroadcastFormSubmissionState =

923

| {

924

id: string;

925

submissionAttempt: number;

926

successful: false;

927

stage: 'validateAllFields' | 'validate';

928

errors: any[];

929

}

930

| {

931

id: string;

932

submissionAttempt: number;

933

successful: false;

934

stage: 'inflight';

935

onError: unknown;

936

}

937

| {

938

id: string;

939

submissionAttempt: number;

940

successful: true;

941

};

942

943

/**

944

* Form unmounted event payload

945

*/

946

type BroadcastFormUnmounted = {

947

/** Form identifier that was unmounted */

948

id: string;

949

};

950

951

/**

952

* Request form state event payload

953

*/

954

type RequestFormState = {

955

/** Form identifier to request state for */

956

id: string;

957

};

958

959

/**

960

* Request form reset event payload

961

*/

962

type RequestFormReset = {

963

/** Form identifier to reset */

964

id: string;

965

};

966

967

/**

968

* Request form force reset event payload

969

*/

970

type RequestFormForceReset = {

971

/** Form identifier to force reset */

972

id: string;

973

};

974

975

/**

976

* Event client event map type

977

* Maps event names to their payload types

978

*/

979

type EventClientEventMap = keyof {

980

'form-devtools:form-state-change': BroadcastFormState;

981

'form-devtools:form-submission-state-change': BroadcastFormSubmissionState;

982

'form-devtools:form-unmounted': BroadcastFormUnmounted;

983

'form-devtools:request-form-state': RequestFormState;

984

'form-devtools:request-form-reset': RequestFormReset;

985

'form-devtools:request-form-force-submit': RequestFormForceReset;

986

};

987

988

/**

989

* Extracted event names from event client event map

990

*/

991

type EventClientEventNames =

992

| 'form-state-change'

993

| 'form-submission-state-change'

994

| 'form-unmounted'

995

| 'request-form-state'

996

| 'request-form-reset'

997

| 'request-form-force-submit';

998

```

999

1000

**Note:** The Event Client is primarily for internal use by TanStack Form DevTools. Most applications do not need to interact with it directly.

1001