TanStack Form is the ultimate solution for handling forms in web applications, providing a powerful and flexible approach to form management. Designed with first-class TypeScript support, headless UI components, and a framework-agnostic design, it streamlines form handling and ensures a seamless experience across various front-end frameworks.
TanStack Form is in release candidate mode, meaning that there should be extremely few or no breaking changes prior to a 1.x release.
To learn more and see our roadmap to v1, please check our GitHub.
Most web frameworks do not offer a comprehensive solution for form handling, leaving developers to create their own custom implementations or rely on less-capable libraries. This often results in a lack of consistency, poor performance, and increased development time. TanStack Form aims to address these challenges by providing an all-in-one solution for managing forms that is both powerful and easy to use.
With TanStack Form, developers can tackle common form-related challenges such as:
By providing a complete solution for these challenges, TanStack Form empowers developers to build robust and user-friendly forms with ease.
In the example below, you can see TanStack Form in action with the React framework adapter:
import * as React from 'react'import { createRoot } from 'react-dom/client'import { useForm } from '@tanstack/react-form'import type { FieldApi } from '@tanstack/react-form'
function FieldInfo({ field }: { field: FieldApi<any, any, any, any> }) { return ( <> {field.state.meta.isTouched && field.state.meta.errors.length ? ( <em>{field.state.meta.errors.join(", ")}</em> ) : null} {field.state.meta.isValidating ? 'Validating...' : null} </> )}
export default function App() { const form = useForm({ defaultValues: { firstName: '', lastName: '', }, onSubmit: async ({ value }) => { // Do something with form data console.log(value) }, })
return ( <div> <h1>Simple Form Example</h1> <form onSubmit={(e) => { e.preventDefault() e.stopPropagation() form.handleSubmit() }} > <div> {/* A type-safe field component*/} <form.Field name="firstName" validators={{ onChange: ({ value }) => !value ? 'A first name is required' : value.length < 3 ? 'First name must be at least 3 characters' : undefined, onChangeAsyncDebounceMs: 500, onChangeAsync: async ({ value }) => { await new Promise((resolve) => setTimeout(resolve, 1000)) return ( value.includes('error') && 'No "error" allowed in first name' ) }, }} children={(field) => { // Avoid hasty abstractions. Render props are great! return ( <> <label htmlFor={field.name}>First Name:</label> <input id={field.name} name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} /> <FieldInfo field={field} /> </> ) }} /> </div> <div> <form.Field name="lastName" children={(field) => ( <> <label htmlFor={field.name}>Last Name:</label> <input id={field.name} name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} /> <FieldInfo field={field} /> </> )} /> </div> <form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]} children={([canSubmit, isSubmitting]) => ( <button type="submit" disabled={!canSubmit}> {isSubmitting ? '...' : 'Submit'} </button> )} /> </form> </div> )}
const rootElement = document.getElementById('root')!
createRoot(rootElement).render(<App />)
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.