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 rerenders, 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 a framework agnostic design that appeals to projects where forms are complex and TypeScript correctness is a priority.
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 outside React.
- The performance difference matters most in forms with many fields. For a login form with three fields, it does not matter. For a data entry form with 40 fields, 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 | Rerenders | 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 rerender of its parent component (and potentially all children) on every keystroke. In a form with 20 fields, this is 20 rerenders 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 rerender. 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 to 300 rerenders per second during active input. After the migration, rerenders during input dropped to near zero and the form felt visibly 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.
<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 a 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 design system that spans multiple frameworks 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
1. 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.
2. 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.
3. 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.
4. Overengineering field level validation for forms that only need validation at submit time. React Hook Form's default mode is `onSubmit` validation, which is the right default for most forms.
5. 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 three 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.
- The State Management Decision in 2025
- The TypeScript Strictness Level That Pays Off
FAQ
## Frequently asked
- Which React form library should I use in 2025?
- What is the performance difference between React Hook Form and Formik?
- How does validation work with React Hook Form?
- What does TanStack Form do differently?
- How do I handle nested and dynamic form fields in React Hook Form?
Author
## 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.
[Start the conversation](/contact) [See the work](/projects) [DM on Instagram](https://www.instagram.com/yashveerlabs/)