The Forms Problem: React Hook Form vs Formik vs TanStack Form
React Hook Form, Formik, and TanStack Form are the three dominant libraries for managing form state in React applications. React Hook Form is the performance default: uncontrolled inputs with minimal re-renders, strong TypeScript support, and the largest ecosystem. Formik is the original standard, now largely superseded by React Hook Form for new projects. TanStack Form is the newest entrant with a type-safe API and framework-agnostic design that appeals to projects where forms are complex and TypeScript correctness is a priority.
Written by Yashveer Singh, founder of Yashveer Labs.
What you actually need to know
- React Hook Form is the correct default choice for new React projects. It has better performance, better TypeScript support, and a larger ecosystem than Formik.
- Formik is not wrong -- it is mature and stable. But for a new project, there is no reason to choose it over React Hook Form.
- TanStack Form is worth evaluating if your forms are complex and nested, if TypeScript correctness is a high priority, or if you are working in a non-React framework.
- The performance difference matters most in forms with many fields. For a 3-field login form, it does not matter. For a 40-field data entry form, React Hook Form's uncontrolled approach has a measurable impact.
- All three libraries support Zod validation. This is not a differentiator -- it is table stakes.
| Library | Re-renders | TypeScript | Bundle Size | Ecosystem | Best For |
|---|---|---|---|---|---|
| React Hook Form | Minimal (uncontrolled) | Strong | ~9kb | Large | Default choice |
| Formik | Many (controlled) | Moderate | ~15kb | Large (aging) | Legacy maintenance |
| TanStack Form | Minimal (uncontrolled) | Excellent | ~12kb | Growing | Complex/nested forms |
The core argument
Forms are one of the most common sources of performance problems in React applications. Every controlled input that stores its value in React state triggers a re-render of its parent component (and potentially all children) on every keystroke. In a form with 20 fields, this is 20 re-renders per keystroke, which is noticeable in the browser. The solution is uncontrolled inputs -- inputs that store their state in the DOM rather than in React, and only involve React when the form is submitted or validated.
React Hook Form is built on this principle. The name reflects the architecture: it uses React hooks to manage the form lifecycle, but the inputs themselves are uncontrolled. The result is a library that performs as well as native HTML forms while adding the validation, error state management, and submit handling that production forms require.
Formik takes the controlled approach. It stores every field value in React state, which means every keystroke triggers a re-render. Formik introduced the field component abstraction to mitigate this (by memoizing fields), but the underlying model is controlled inputs, and the performance ceiling is lower than React Hook Form's.
I have used both libraries extensively. On a project for Expert Tutorials -- a content platform with complex multi-step registration forms -- I migrated a form from Formik to React Hook Form after profiling showed that a 15-field form was triggering 200-300 re-renders per second during active input. After the migration, re-renders during input dropped to near zero and the form felt visalpably faster. This is an extreme case, but the same improvement applies to any form with more than a handful of fields.
React Hook Form: the implementation
The core API of React Hook Form is straightforward. You call useForm with a type parameter (or let TypeScript infer it from the defaultValues), then register each input with the register function. The register function returns event handlers and a ref that connects the input to the form.
```tsx const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();
<input {...register('email', { required: 'Email is required' })} /> ```
Validation is added inline through the register options or through a resolver. The Zod resolver is the recommended pattern for forms with complex validation logic:
```tsx const schema = z.object({ email: z.string().email(), password: z.string().min(8), });
const { register, handleSubmit, formState: { errors } } = useForm<z.infer<typeof schema>>({ resolver: zodResolver(schema), }); ```
The formState object contains errors, isSubmitting, isValid, isDirty, and other useful state. The handleSubmit wrapper handles the submit event and calls your callback only when validation passes.
For dynamic fields, useFieldArray adds and removes fields at runtime with stable identities:
``tsx const { fields, append, remove } = useFieldArray({ control, name: 'items' }); ``
Formik: the established pattern
Formik's API centers on the useFormik hook (or the <Formik> render prop component). It manages form values, touched state, and errors in React state, and provides a handleChange, handleBlur, and handleSubmit API:
``tsx const formik = useFormik({ initialValues: { email: '' }, validate: values => { const errors = {}; if (!values.email) errors.email = 'Required'; return errors; }, onSubmit: values => { /* submit */ }, }); ``
Formik also supports Yup and Zod through a validationSchema prop. The API is familiar and well-documented. The reason to choose it over React Hook Form is not the API -- it is that you are working in an existing codebase that uses Formik and a migration is not worth the effort.
For new projects, Formik's controlled-input model, larger bundle size, and aging ecosystem make it a weaker default than React Hook Form. The documentation has not been updated as actively as React Hook Form's, and some ecosystem libraries have prioritized React Hook Form support.
TanStack Form: the type-safe alternative
TanStack Form (the successor to React Form) takes a different architectural approach. Rather than a hook that manages all fields, TanStack Form uses a form factory pattern where each field is a typed entity:
```tsx const form = useForm({ defaultValues: { email: '', age: 0 }, });
<form.Field name="email" children={field => ( <input value={field.state.value} onChange={e => field.handleChange(e.target.value)} /> )} /> ```
The type inference from defaultValues means that field.name is typed to the keys of the form object, and field.state.value is typed to the value type for that field. This level of type safety prevents category of errors that React Hook Form's string-based register field name does not catch.
TanStack Form also has first-class server-side validation support and framework adapters for React, Vue, and Solid. If you are building a multi-framework design system or a project where TypeScript correctness is a high priority, TanStack Form is worth the API verbosity.
The validation library question: Zod vs Yup
All three form libraries support both Zod and Yup for schema-based validation. Zod is the better choice for new projects. It has better TypeScript inference (the inferred type from a Zod schema is exact, not approximate), a more expressive API for complex transformations, and a growing ecosystem that treats it as a first-class integration target.
Yup is mature and has a slightly more readable syntax for simple schemas, but Zod's type inference advantage is significant enough that for any project where TypeScript is the primary language, Zod is the default.
Common mistakes engineers make with form libraries
- Using controlled inputs and then wondering why the form is slow. If you are using Formik or building your own form state with
useState, profile the render count before assuming the form library is the problem. - Not using a resolver. Inline validation rules are sufficient for simple forms but become unmanageable for complex forms. A Zod schema as the single source of validation truth is cleaner.
- Forgetting to handle the error state in the UI. Validation running silently without error messages is a common cause of form submissions that fail with no feedback to the user.
- Over-engineering field-level validation for forms that only need submit-time validation. React Hook Form's default mode is
onSubmitvalidation, which is the right default for most forms. - Not testing form behavior with keyboard navigation. Screen reader and keyboard accessibility for forms requires specific ARIA attributes that form libraries do not add automatically.
Where to start: a 3-step form library decision
Step 1: For any new project, start with React Hook Form and the zodResolver. Install react-hook-form, zod, and @hookform/resolvers. Define your form schema with Zod, wire it to React Hook Form, and build the form. This stack covers 90 percent of form requirements with minimal configuration.
Step 2: If the form has complex nested structures or if type safety at the field level is a priority, evaluate TanStack Form. The API is more verbose but the type inference is more complete. This is the right choice for applications where forms are central to the product.
Step 3: If you are maintaining an existing Formik project, profile before migrating. If the forms are performant and the team is productive with Formik, the migration cost is not justified. If there are performance complaints or the team is struggling with the API, migrate the most problematic forms first and evaluate the impact.
The Form Library That Stays Out of the Way
Yashveer Singh. Founder of Yashveer Labs. The form library I reach for first on new projects is React Hook Form with Zod. I used it on the Velmora project for a multi-step onboarding flow with conditional fields and async validation -- the combination of uncontrolled inputs for performance and Zod for type-safe validation handled everything the design required without any workarounds. For the Expert Tutorials platform, the migration from Formik to React Hook Form on the registration form was the simplest performance improvement I made in the project.
Related reading
- The Frontend Architecture That Survives Three Years of Feature Sprawl
- The Frontend Testing Strategy That Works
- The State Management Decision in 2025
- The TypeScript Strictness Level That Pays Off
Frequently asked
The person who wrote this
Yashveer Singh wrote this. Class 12, Commerce track, full stack developer. The categories do not align, which is the point. The work runs in production. Everything else is paperwork. If the project on your plate is the one this article describes, you can reach me through the contact page or through Instagram. I will read it. I will reply. That is the standard.
Posts that line up with this one.
- Web App and Frontend Development
The Frontend Architecture That Survives Three Years of Feature Sprawl
The structural decisions that keep a React frontend maintainable after three years and dozens of features -- and the patterns that cause it to collapse.
- Web App and Frontend Development
The Modern Web App Stack: A 2026 Survey
The full-stack choices that are stable, well-supported, and worth learning in 2026 -- from framework to database to deployment.
- Web App and Frontend Development
Loading States, Skeletons, and Optimistic UI
How you handle loading states is one of the most visible indicators of product quality. Here is the decision framework for when to use spinners, skeletons, and optimistic updates, and the common mistakes that make apps feel slow.
- Web App and Frontend Development
Modal Patterns That Do Not Trap Users
Modals are overused, frequently misimplemented, and a common source of user frustration. Here is how to design and build modals that provide the right information at the right time without trapping users or creating accessibility failures.