3.5 KiB
3.5 KiB
TanStack Form Reference
Setup
npm i @tanstack/react-form
npm i @tanstack/zod-form-adapter zod # optional: Zod validation
useForm
const form = useForm({
defaultValues: { name: '', email: '', age: 0 },
validatorAdapter: zodValidator, // optional global adapter
onSubmit: async ({ value }) => {
// value is fully typed: { name: string, email: string, age: number }
await api.createUser(value)
},
})
form.Field
<form.Field
name="email"
validators={{
onChange: z.string().email('Invalid email'),
onBlur: ({ value }) => !value ? 'Required' : undefined,
onBlurAsync: async ({ value }) => {
const taken = await checkEmailTaken(value)
return taken ? 'Email already taken' : undefined
},
onBlurAsyncDebounceMs: 500,
}}
>
{(field) => (
<div>
<input
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
{field.state.meta.errors.map((err) => <p key={err}>{err}</p>)}
</div>
)}
</form.Field>
Validation Events
| Event | When | Use Case |
|---|---|---|
onChange |
Every keystroke | Format validation |
onBlur |
Field loses focus | Required checks |
onBlurAsync |
Field loses focus | Server-side checks |
onSubmit |
Form submission | Final validation |
onSubmitAsync |
Form submission | Async final validation |
All support Zod schemas or inline functions returning string | undefined.
form.Subscribe — Reactive UI
<form.Subscribe selector={(s) => [s.canSubmit, s.isSubmitting]}>
{([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? 'Saving...' : 'Submit'}
</button>
)}
</form.Subscribe>
Nested & Array Fields
// Nested object
<form.Field name="address.city">{...}</form.Field>
// Array field
<form.Field name="tags" mode="array">
{(field) => (
<>
{field.state.value.map((_, i) => (
<form.Field key={i} name={`tags[${i}]`}>
{(subField) => <input value={subField.state.value} ... />}
</form.Field>
))}
<button onClick={() => field.pushValue('')}>Add Tag</button>
</>
)}
</form.Field>
Server-Side Validation (TanStack Start)
// server
import { createServerValidate } from '@tanstack/react-form/start'
const serverValidate = createServerValidate({
validatorAdapter: zodValidator,
onServerValidate: z.object({ email: z.string().email() }),
})
// client — merge server errors into form state
import { mergeForm, useTransform } from '@tanstack/react-form'
useTransform((state) => mergeForm(state, serverState), [serverState])
Form State Properties
| Property | Type | Description |
|---|---|---|
values |
T |
Current form values |
errors |
string[] |
Form-level errors |
canSubmit |
boolean |
No errors + not submitting |
isSubmitting |
boolean |
Submission in progress |
isDirty |
boolean |
Values differ from defaults |
isTouched |
boolean |
Any field touched |
isValid |
boolean |
No validation errors |
Field State Meta
| Property | Type | Description |
|---|---|---|
errors |
string[] |
Field validation errors |
isTouched |
boolean |
Field was blurred |
isDirty |
boolean |
Value changed from default |
Schema Adapters
@tanstack/zod-form-adapter— Zod@tanstack/valibot-form-adapter— Valibot@tanstack/yup-form-adapter— Yup (community)