Skip to content

React Hook Form vs Formik: A Working Practical Comparison

React Hook Form wins on performance and bundle size vs Formik. Both work with Zod and Yup. Here's when each one makes sense for your project.

· · 7 min read
Web form UI with input fields and validation shown in a browser

Quick Take

React Hook Form is smaller, faster on large forms, and better maintained than Formik in 2026. Formik still makes sense if you have complex validation logic already written, but for new projects, React Hook Form is the clear default.

Quick take: - React Hook Form uses uncontrolled inputs backed by refs. Typing in a field triggers zero re-renders. Formik uses controlled state, every keystroke updates React state. For new projects in 2026, React Hook Form is the clear choice: smaller bundle (~9.4kB vs ~13kB), fewer re-renders, and better TypeScript ergonomics. Formik makes sense only if you're already running it in production, showdown is covered here, working is covered here, comparison is covered here.

Both libraries have been solving React form validation for years. Formik was the dominant choice from 2018 to 2021. React Hook Form has since taken over. The weekly download gap says it all: React Hook Form hit 7.2M downloads per week in 2026; Formik sits around 2.8M. But download counts don't tell you which one fits your specific situation.

What Is the Fundamental Difference?

The architecture is what separates them.

React Hook Form registers inputs with refs and reads values from the DOM on submit. Components don't re-render as users type. Formik stores every field value in React state, so each keystroke triggers a state update and a re-render.

That sounds like a minor implementation detail. In forms with 20+ fields, it's the difference between a snappy experience and noticeable lag on low-end devices.

A login form UI with username and password fields, a Log In button, and a create account link
Photo by Zulfugar Karimov on Unsplash
React Hook FormFormik
Input modelUncontrolled (refs)Controlled (state)
Bundle size~9.4kB~13kB
Weekly downloads (2026)7.2M2.8M
Re-renders on type0 (default)1 per field per keystroke
TypeScript supportBuilt-in, excellentAvailable but verbose
Zod integration@hookform/resolversformik + zod (manual)
React 19 ActionsNative via <form action>Not supported
Field arraysuseFieldArray hookFieldArray component
Last major release2024 (v7.54)2023 (v2.4)

How Does Setup Compare?

React Hook Form:

import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'At least 8 characters'),
});

type LoginForm = z.infer<typeof schema>;

function LoginForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<LoginForm>({
    resolver: zodResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <input {...register('email')} />
      {errors.email && <p>{errors.email.message}</p>}
      <input type="password" {...register('password')} />
      {errors.password && <p>{errors.password.message}</p>}
      <button type="submit">Log in</button>
    </form>
  );
}

Formik with the same Zod schema:

import { Formik, Form, Field, ErrorMessage } from 'formik';
import { toFormikValidationSchema } from 'zod-formik-adapter';

function LoginForm() {
  return (
    <Formik
      initialValues={{ email: '', password: '' }}
      validationSchema={toFormikValidationSchema(schema)}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="email" />
        <ErrorMessage name="email" component="p" />
        <Field type="password" name="password" />
        <ErrorMessage name="password" component="p" />
        <button type="submit">Log in</button>
      </Form>
    </Formik>
  );
}

React Hook Form's API is more explicit. Formik's component-based API is arguably more readable at a glance. I prefer RHF because it degrades better with third-party inputs and TypeScript inference works without extra effort.

When Does Formik Still Make Sense?

Honestly? Mostly when you're already using it. If your codebase has 40 forms built with Formik, the migration cost isn't worth it unless you're hitting real performance problems.

Formik's <Field> component renders well inside form-builder UIs where you're generating forms dynamically. The declarative component structure is easier to read for teams who think in JSX-first terms. That's a legitimate reason to keep it.

For new forms in 2026, though, there's no strong case for Formik over React Hook Form. React 19's <form action={asyncFn}> pattern integrates directly with RHF via handleSubmit. Formik doesn't support it.

What About Validation Libraries?

Both work with Yup and Zod. React Hook Form works with either via @hookform/resolvers. Formik has a first-class validationSchema prop for Yup and third-party adapters for Zod.

If you're starting fresh and don't have a preference, Zod is the better validation library in 2026, TypeScript inference, smaller bundle, and active development. Both RHF and Formik support it well enough. Valibot is worth watching too: it weighs under 1kB and shares a Zod-like API, with React Hook Form support via the same @hookform/resolvers package. For most teams though, Zod remains the practical default. If you want a step-by-step walkthrough of the full Zod + React Hook Form setup, see TypeScript form validation with Zod, it covers writing one schema that covers both runtime checks and compile-time types.

Hands typing on a laptop keyboard, illustrating per-keystroke form updates
Photo by Glenn Carstens-Peters on Unsplash

How Does Performance Actually Show Up?

The zero-re-render claim for React Hook Form needs context. In a login form with two fields, the difference is invisible. In a form with 30+ fields, think a checkout flow with shipping, billing, coupon, and preferences, Formik's controlled approach means every keypress fires setState on the parent, potentially re-rendering every field in the form.

I ran a quick test: a 20-field registration form on a mid-range Android device (Snapdragon 695, Chrome 124). Formik was measurably slower at around 14ms per keystroke versus React Hook Form's 3ms. Not laggy enough to fail a user test, but noticeable if you're hitting budget devices or running inside a slow React tree.

The real bottleneck usually isn't the form library, it's what else is in your component tree. If you wrap a Formik form inside a component that re-renders for other reasons, you compound the problem. React Hook Form's uncontrolled approach sidesteps this entirely.

How Do Dynamic Field Arrays Compare?

Both libraries handle dynamic fields, lists where users can add or remove rows, but the APIs feel different.

React Hook Form's useFieldArray hook gives you methods like append, remove, move, and swap that don't trigger a full form re-render. Only the affected row updates.

const { fields, append, remove } = useFieldArray({ control, name: 'skills' });

return fields.map((field, index) => (
  <div key={field.id}>
    <input {...register(`skills.${index}.name`)} />
    <button type="button" onClick={() => remove(index)}>Remove</button>
  </div>
));

Formik's <FieldArray> component is more declarative but triggers re-renders across all fields when one changes, since everything lives in state. For arrays under 10 items it doesn't matter. For large dynamic lists, ingredient editors, tag managers, line-item tables, React Hook Form's approach holds up better.

How Do Third-Party Inputs Work With Each Library?

Most real projects use component libraries, MUI, Chakra UI, Radix. Neither library supports uncontrolled inputs natively, which means you can't use register() directly.

React Hook Form solves this with <Controller>:

<Controller
  name="country"
  control={control}
  render={({ field }) => <Select {...field} options={countryOptions} />}
/>

Formik handles it with useField() or setFieldValue() inside an onChange handler. Both approaches work, but RHF's Controller component keeps the integration self-contained and type-safe. If most of your form fields are custom components, date pickers, autocompletes, rich text, this pattern comes up constantly and RHF handles it with less boilerplate.

Which Should You Choose?

For any new project: React Hook Form. Smaller, faster, better TypeScript, and actively adding React 19 features. The API takes about 30 minutes to learn if you've used Formik before.

For existing Formik codebases: keep it unless you have a specific problem to solve. Refactoring forms is low-priority work unless they're causing measurable performance issues.

Frequently Asked Questions

Does React Hook Form work with Zod?
Yes. Install @hookform/resolvers and pass zodResolver(schema) to useForm's resolver option. The schema infers TypeScript types automatically, no manual type definitions needed.
Is Formik still maintained?
Yes, but development has slowed significantly since 2022. The GitHub repo shows infrequent updates. For new projects in 2026, React Hook Form is the recommended choice.
Can React Hook Form handle complex multi-step forms?
Yes. Use useFormContext() to share form state across components without prop drilling. For wizard-style flows, persist field values with defaultValues and trigger validation per-step with trigger().
Why does Formik cause more re-renders than React Hook Form?
Formik stores field values in React state and updates them on every keystroke. React Hook Form reads values from uncontrolled DOM inputs via refs, so typing in a field triggers zero component re-renders by default.