Using with TanStack Form
Connect StackForm to TanStack Form via TanstackFormProvider
Install
npm install @stackform/core @stackform/ui @stackform/tanstack @tanstack/react-formRequires TanStack Form v1.
Quick start
A complete form with three fields, error display, and submission:
import { useForm } from '@tanstack/react-form'
import { StackFormProvider, TextField, CheckboxField } from '@stackform/ui'
import { TanstackFormProvider } from '@stackform/tanstack'
export function ContactForm() {
const form = useForm({
defaultValues: { name: '', email: '', terms: false },
onSubmit: async ({ value }) => {
console.log('Submitted:', value)
},
})
return (
<StackFormProvider>
<TanstackFormProvider form={form}>
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
className="space-y-4"
>
<TextField name="name" label="Name" placeholder="Your name" />
<TextField
name="email"
label="Email"
type="email"
placeholder="you@example.com"
hint="We'll never share your email."
/>
<CheckboxField name="terms" label="I agree to the terms and conditions" />
<button type="submit" disabled={form.state.isSubmitting}>
{form.state.isSubmitting ? 'Submitting…' : 'Submit'}
</button>
</form>
</TanstackFormProvider>
</StackFormProvider>
)
}Provider setup
Pass your TanStack form instance directly to TanstackFormProvider. The provider subscribes to the form store and injects the resolver and form state into StackForm context:
import { useForm } from '@tanstack/react-form'
import { TanstackFormProvider } from '@stackform/tanstack'
const form = useForm({ defaultValues: { name: '' } })
<TanstackFormProvider form={form}>
{/* field components here */}
</TanstackFormProvider>StackFormProvider must wrap TanstackFormProvider. The form prop accepts any TanStack AnyFormApi — the provider is not tied to a specific schema library.
Error display
Errors surface automatically whenever TanStack Form sets them. Configure validation in your useForm call or via per-field validators — StackForm displays whatever the form library sets, with no additional wiring.
const form = useForm({
defaultValues: { email: '' },
validators: {
onChange: ({ value }) => {
if (!value.email.includes('@')) {
return { fields: { email: 'Enter a valid email address' } }
}
},
},
})
// TextField renders the error automatically when validation fails
<TextField name="email" label="Email" />TanStack Form validates on onChange by default. To validate only on submit, set validationTrigger: 'submit' in your useForm options.
Form submission
Call form.handleSubmit() inside your <form> element's onSubmit. Both preventDefault and stopPropagation are needed when forms are nested. The onSubmit callback defined in useForm receives the validated values.
const form = useForm({
defaultValues: { name: '', email: '' },
onSubmit: async ({ value }) => {
await saveToApi(value)
},
})
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
{/* fields */}
<button type="submit" disabled={form.state.isSubmitting}>
{form.state.isSubmitting ? 'Saving…' : 'Save'}
</button>
</form>Validation differences vs React Hook Form
| Behaviour | React Hook Form | TanStack Form |
|---|---|---|
| Default validation trigger | submit | onChange |
| Per-field validators | via register rules or resolver | via validators on the field |
| Async validation | resolver | asyncValidators on the field |
| Submission handler | handleSubmit(fn) on <form> | onSubmit in useForm options |
Configure validation timing in your TanStack useForm call or per-field validators option — not in StackForm.