/web-forms-tanstack-form
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety
$ npx -y skills add agents-inc/skills --skill web-forms-tanstack-form --agent claude-codeHow 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.
- 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.mdname: web-forms-tanstack-form
description: TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety
TanStack Form Patterns
> **Quick Guide:** Use `useForm` with `defaultValues` and typed generics. Render fields with `form.Field` using the render-prop `children` pattern. Validation lives in the `validators` prop on both form and field level — use `onChange`, `onBlur`, `onSubmit` (sync) and their `Async` variants. Use `mode="array"` for dynamic field lists with `pushValue`/`removeValue`. Use `onChangeListenTo` for cross-field validation. For app-wide consistency, create a shared `useAppForm` via `createFormHook`. Always provide `defaultValues` — TanStack Form infers types from them.
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST provide `defaultValues` to `useForm` — TanStack Form infers field types from them)**
**(You MUST use `form.Field` with the `children` render prop — TanStack Form does not use `register` or `Controller`)**
**(You MUST use the `validators` prop for validation — NOT inline `rules` or external resolver wrappers)**
**(You MUST handle `field.state.meta.errors` as an array — always `.map()` over errors)**
**(You MUST call `form.handleSubmit()` inside the form's `onSubmit` handler with `e.preventDefault()`)**
</critical_requirements>
---
**Auto-detection:** TanStack Form, @tanstack/react-form, @tanstack/vue-form, @tanstack/solid-form, @tanstack/angular-form, @tanstack/lit-form, useForm from tanstack, form.Field, createFormHook, createFormHookContexts, useAppForm, fieldContext, formContext, handleSubmit tanstack, pushValue, removeValue, onChangeListenTo, field.handleChange, field.handleBlur, field.state, formDevtoolsPlugin
**When to use:**
- Building type-safe forms where field types are inferred from `defaultValues`
- Managing complex validation with sync, async, and cross-field rules
- Dynamic forms with add/remove field groups (array fields)
- Multi-framework projects (React, Vue, Solid, Angular, Lit)
- Projects already using the TanStack ecosystem
**When NOT to use:**
- Single input without validation (use native state)
- Server-only forms with server actions (use native form + action)
- Read-only data display (not a form scenario)
---
Table of Contents
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Basic form, Field component, TypeScript, form submission
- [examples/validation.md](examples/validation.md) - Sync/async validation, validator adapters, form-level validation
- [examples/arrays.md](examples/arrays.md) - Dynamic array fields with pushValue/removeValue
- [examples/composition.md](examples/composition.md) - createFormHook, useAppForm, listeners, side effects
- [reference.md](reference.md) - API tables, validator events, decision frameworks
---
<philosophy>
Philosophy
TanStack Form is **headless and type-safe by design**. It owns zero UI — you render every input yourself. The library provides form state, validation orchestration, and field management. Types flow from `defaultValues` through every field name, value, and error — no manual generics required (though you can provide them).
**Core Principles:**
1. **Type inference from defaults** - `defaultValues` defines the form shape; field names and values are fully typed 2. **Headless** - Zero UI opinions; works with any component library or native inputs 3. **Validation-event-driven** - Validators attach to specific events (`onChange`, `onBlur`, `onSubmit`) per field or per form 4. **Framework-agnostic core** - Same mental model across React, Vue, Solid, Angular, and Lit 5. **Composition via factory** - `createFormHook` shares field/form components across an app
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Basic useForm + form.Field
Every form starts with `useForm` and renders fields via `form.Field`. The `children` render prop receives the field API with `state`, `handleChange`, and `handleBlur`.
import { useForm } from "@tanstack/react-form";
const form = useForm({
defaultValues: { name: "", email: "" },
onSubmit: async ({ value }) => {
await submitToApi(value);
},
});
return (
<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>
);**Key difference from other form libraries:** No `register`, no `Controller`, no `ref` forwarding. You always use `field.handleChange` and `field.state.value` explicitly.
See [examples/core.md](examples/core.md) for complete form with error display and accessibility.
---
Pattern 2: Field-Level Validation
Validators are functions on the `validators` prop. Sync validators return a string (error) or `undefined` (valid). Async validators use `onChangeAsync`, `onBlurAsync`, `onSubmitAsync`.
<form.Field
name="age"
validators={{
onChange: ({ value }) => (value < 13 ? "Must be 13 or older" : undefined),
onBlurAsync: async ({ value }) => {
const exists = await checkAge(value);
return exists ? undefined : "Age not valid on server";
},
}}
children={(field) => (
<div>
<input
type="number"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.valueAsNumber)}
/>
{field.state.meta.errors.map((err) => (
<em key={err} role="alert">
{err}
</em>
))}
</div>
)}
/>**Sync-first gating:** When both `onBlur` and `onBlurAsync` exist, the async validator only runs if the sync validator passes. Same for `onChange`/
Read more
name: web-forms-tanstack-form description: TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety
TanStack Form Patterns
> **Quick Guide:** Use `useForm` with `defaultValues` and typed generics. Render fields with `form.Field` using the render-prop `children` pattern. Validation lives in the `validators` prop on both form and field level — use `onChange`, `onBlur`, `onSubmit` (sync) and their `Async` variants. Use `mode="array"` for dynamic field lists with `pushValue`/`removeValue`. Use `onChangeListenTo` for cross-field validation. For app-wide consistency, create a shared `useAppForm` via `createFormHook`. Always provide `defaultValues` — TanStack Form infers types from them.
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST provide `defaultValues` to `useForm` — TanStack Form infers field types from them)**
**(You MUST use `form.Field` with the `children` render prop — TanStack Form does not use `register` or `Controller`)**
**(You MUST use the `validators` prop for validation — NOT inline `rules` or external resolver wrappers)**
**(You MUST handle `field.state.meta.errors` as an array — always `.map()` over errors)**
**(You MUST call `form.handleSubmit()` inside the form's `onSubmit` handler with `e.preventDefault()`)**
</critical_requirements>
---
**Auto-detection:** TanStack Form, @tanstack/react-form, @tanstack/vue-form, @tanstack/solid-form, @tanstack/angular-form, @tanstack/lit-form, useForm from tanstack, form.Field, createFormHook, createFormHookContexts, useAppForm, fieldContext, formContext, handleSubmit tanstack, pushValue, removeValue, onChangeListenTo, field.handleChange, field.handleBlur, field.state, formDevtoolsPlugin
**When to use:**
- Building type-safe forms where field types are inferred from `defaultValues`
- Managing complex validation with sync, async, and cross-field rules
- Dynamic forms with add/remove field groups (array fields)
- Multi-framework projects (React, Vue, Solid, Angular, Lit)
- Projects already using the TanStack ecosystem
**When NOT to use:**
- Single input without validation (use native state)
- Server-only forms with server actions (use native form + action)
- Read-only data display (not a form scenario)
---
Table of Contents
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Basic form, Field component, TypeScript, form submission
- [examples/validation.md](examples/validation.md) - Sync/async validation, validator adapters, form-level validation
- [examples/arrays.md](examples/arrays.md) - Dynamic array fields with pushValue/removeValue
- [examples/composition.md](examples/composition.md) - createFormHook, useAppForm, listeners, side effects
- [reference.md](reference.md) - API tables, validator events, decision frameworks
---
<philosophy>
Philosophy
TanStack Form is **headless and type-safe by design**. It owns zero UI — you render every input yourself. The library provides form state, validation orchestration, and field management. Types flow from `defaultValues` through every field name, value, and error — no manual generics required (though you can provide them).
**Core Principles:**
1. **Type inference from defaults** - `defaultValues` defines the form shape; field names and values are fully typed 2. **Headless** - Zero UI opinions; works with any component library or native inputs 3. **Validation-event-driven** - Validators attach to specific events (`onChange`, `onBlur`, `onSubmit`) per field or per form 4. **Framework-agnostic core** - Same mental model across React, Vue, Solid, Angular, and Lit 5. **Composition via factory** - `createFormHook` shares field/form components across an app
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Basic useForm + form.Field
Every form starts with `useForm` and renders fields via `form.Field`. The `children` render prop receives the field API with `state`, `handleChange`, and `handleBlur`.
import { useForm } from "@tanstack/react-form";
const form = useForm({
defaultValues: { name: "", email: "" },
onSubmit: async ({ value }) => {
await submitToApi(value);
},
});
return (
<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>
);**Key difference from other form libraries:** No `register`, no `Controller`, no `ref` forwarding. You always use `field.handleChange` and `field.state.value` explicitly.
See [examples/core.md](examples/core.md) for complete form with error display and accessibility.
---
Pattern 2: Field-Level Validation
Validators are functions on the `validators` prop. Sync validators return a string (error) or `undefined` (valid). Async validators use `onChangeAsync`, `onBlurAsync`, `onSubmitAsync`.
<form.Field
name="age"
validators={{
onChange: ({ value }) => (value < 13 ? "Must be 13 or older" : undefined),
onBlurAsync: async ({ value }) => {
const exists = await checkAge(value);
return exists ? undefined : "Age not valid on server";
},
}}
children={(field) => (
<div>
<input
type="number"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.valueAsNumber)}
/>
{field.state.meta.errors.map((err) => (
<em key={err} role="alert">
{err}
</em>
))}
</div>
)}
/>**Sync-first gating:** When both `onBlur` and `onBlurAsync` exist, the async validator only runs if the sync validator passes. Same for `onChange`/
Showing the first part of this file.
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?
Repo: agents-inc/skills
Other skills on agents-inc-skills.
- /ai-infrastructure-huggingface-inference
Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints
Open skill - /ai-infrastructure-litellm
LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment
Open skill - /ai-infrastructure-modal
Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns
Open skill - /ai-infrastructure-ollama
Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint
Open skill - /ai-infrastructure-replicate
Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training
Open skill - /ai-infrastructure-together-ai
Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints
Open skill

