Skip to content
Development
Skill

/web-forms-tanstack-form

TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-forms-tanstack-form --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-forms-tanstack-form

Context preview

The summary Claude sees to decide when to auto-load this skill.

TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety

SKILL.md

web-forms-tanstack-form.SKILL.md
name: web-forms-tanstack-form
description: TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety

TanStack Form Patterns

> **Quick Guide:** `useForm` takes `defaultValues`, and every field name, value type and the submit > payload are inferred from that object. Fields render through `form.Field` with a `children` render > prop that supplies `field.state.value`, `field.handleChange` and `field.handleBlur`. Validation > lives in the `validators` prop — keyed by event (`onChange`, `onBlur`, `onSubmit`) with an `Async` > variant of each, on the field or on the form. `mode="array"` unlocks `pushValue`/`removeValue`, > `onChangeListenTo` re-runs a validator when another field changes, and `form.Subscribe` narrows > which state changes re-render what.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — a form end to end: fields, typing, submission, reset
  • [examples/validation.md](examples/validation.md) — sync, async and cross-field validators; schema objects in validators
  • [examples/arrays.md](examples/arrays.md) — dynamic field groups with `mode="array"`
  • [examples/composition.md](examples/composition.md) — `createFormHook`, `useAppForm`, listeners
  • [reference.md](reference.md) — validator events, field and form state tables, API methods, framework packages

---

Which path applies

  • **A single form** — `useForm` plus `form.Field` render props, nothing else to set up. Follow

[examples/core.md](examples/core.md).

  • **Forms across an app that should behave alike** — `createFormHook` registers shared field and

form components once, and `useAppForm` replaces `useForm` at each call site. Follow [examples/composition.md](examples/composition.md).

  • **A framework other than React** — the form core is shared and only the package and the field

binding differ; reference.md's Framework Packages table names both for each.

---

<critical_requirements>

Before writing TanStack Form code

**Give `useForm` a `defaultValues` entry for every field.** Field names, value types and the submit payload are all inferred from that object, so a field missing from it is a field the types do not know about.

**Render every field through `form.Field` and its `children` render prop.** The render prop receives the value and the handlers explicitly — this library has no field-registration helper and does no ref forwarding, so an input wired any other way never joins the form.

**Put validation in the `validators` prop, keyed by the event that should run it.** `onChange`, `onBlur` and `onSubmit` each have an `Async` counterpart, and the same prop exists on the field and on the form.

**Read `field.state.meta.errors` as an array.** It holds every current error for the field, so `.map()` over it or check `.length`; compared against a string it is always unequal.

**Call `e.preventDefault()` in the form's `onSubmit` before `form.handleSubmit()`.** The library does not intercept the native submit, so without it the browser navigates away mid-submission.

</critical_requirements>

---

**Auto-detection:** @tanstack/react-form, @tanstack/vue-form, @tanstack/solid-form, @tanstack/angular-form, @tanstack/lit-form, @tanstack/form-core, form.Field, form.Subscribe, createFormHook, createFormHookContexts, useAppForm, withForm, fieldContext, formContext, field.handleChange, field.handleBlur, field.state.meta, pushValue, removeValue, swapValues, onChangeListenTo, onBlurListenTo, setErrorMap, formDevtoolsPlugin

**Applies to:**

  • Form state, validation timing and submission
  • Cross-field rules, where one field's validity depends on another's value
  • Dynamic lists of field groups that add, remove and reorder
  • Sharing field and form components across an app through the factory
  • Forms in Vue, Solid, Angular or Lit as well as React

**Handled elsewhere:**

  • Authoring the validation schema — a validator accepts any Standard Schema object, and how that

schema states its rules is settled by whatever owns it.

  • Rendering and styling the inputs — this library owns no UI; the render prop hands over the value

and the handlers, and the markup is yours.

  • Where the initial values came from — `defaultValues` is a plain object, and the form fetches

nothing.

---

<philosophy>

The form is headless and its types run on inference. `defaultValues` is the schema of record: field names autocomplete from it, `field.state.value` is typed by it, and the `onSubmit` payload matches it — without a generic parameter, and without a second type declaration that could drift.

Validation is bound to events rather than to a mode. Each validator declares when it runs, at the level it belongs to, so a cheap format check can sit on `onChange` while the expensive uniqueness check waits for `onBlurAsync` on the same field.

State is read by subscription. `form.Subscribe` and `useStore` take a selector and re-render only when what the selector returns changes, so reading `form.state` directly in a component body opts out of the whole design.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: useForm and form.Field

The render prop is the whole field API — value in, handlers out, nothing implicit.

const form = useForm({
  defaultValues: { name: "", email: "" },
  onSubmit: async ({ value }) => {
    await submitToApi(value);
  },
});

<form
  onSubmit={(e) => {
    e.preventDefault();
    form.handleSubmit();
  }}
>
  <form.Field
    name="email"
    children={(field) => (
      <input
        value={field.state.value}
        onBlur={field.handleBlur}
        onChange={(e) => field.handleChange(e.target.value)}
      />
    )}
  />
</form>;

`onBlur={field.handleBlur}` is what marks the field touched — omit it and `isTouched` stays false and any `onBlur` validator never runs.

Full code: [examples/core.md](examples/core.md)

---

Pattern 2: Field-level validators

A sync validator returns a message string, or `undefined` when the value p

Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.