Skip to main content

Command Palette

Search for a command to run...

JavaScript Libraries for Multi-Step Forms: React Hook Form, Formik, TanStack Form, and When to Go Vanilla

Published

Choosing a form management library before writing a multi-step form is one of the most consequential early decisions in a frontend project. The library determines how field state is managed, how validation runs, how errors surface, how resets behave, and how much boilerplate the team writes for every form in the application. A library that fits the team's mental model and the form's requirements will make the multi-step architecture feel natural. A mismatch creates constant friction: workarounds for state sharing across steps, awkward validation triggers, difficult-to-test submission logic. This guide covers the major options available to React developers, examines what each does well and poorly in a multi-step context, and provides a framework for choosing among them.

Library shelves with rows of organized books in a quiet reading room Photo by yangjunjun2 on Pexels

Why Form Libraries Exist

Before comparing specific libraries, it is worth understanding what problem they solve. Building a form entirely in React without a library requires the developer to manage every aspect of field state manually: tracking values in state, updating state on each change event, running validation logic and storing error messages, tracking which fields have been touched or dirtied, handling async validation without race conditions, and resetting all of this state on submission. For a three-field form, this is manageable. For a twenty-field multi-step form with conditional fields, async validation, and cross-field dependencies, the custom implementation grows complex quickly.

Form libraries encapsulate this complexity behind a defined API. They provide patterns for registering fields, subscribing to value changes, triggering validation, reading errors, and resetting state. They also provide performance optimizations -- most notably, React Hook Form uses uncontrolled inputs to minimize re-renders, which makes a meaningful difference in forms with many fields.

The distinction between form libraries and schema validation libraries is also worth establishing. Libraries like Zod and Yup define and validate data shapes. They do not manage form state. Form libraries and schema libraries are typically used together: the form library manages state and triggers validation, and the schema library defines the validation rules. React Hook Form integrates with both Zod and Yup via a resolver adapter. Formik also supports both via a schema validation prop.

React Hook Form

React Hook Form has become the most widely used React form library as of 2025. Its defining characteristic is that it uses uncontrolled inputs by default. Rather than storing every field value in React state (which triggers a re-render on every keystroke), React Hook Form tracks values in refs that live outside the React render cycle. A re-render is triggered only when the component explicitly subscribes to a value via the watch function, or when an error state changes.

For multi-step forms, React Hook Form's approach to state management requires consideration. Because the library manages state in refs rather than a shared React state object, sharing state across step components is not as simple as passing a state object from a parent. The recommended pattern is to lift the form context above all step components using React Hook Form's FormProvider and useFormContext. This makes the form instance accessible in every step component without prop drilling. Field values from completed steps remain in the form instance as the user navigates. Calling getValues() at any point returns all field values across all steps.

Per-step validation in React Hook Form uses the trigger function, which can be called with a list of specific field names rather than the entire schema. Before advancing to the next step, the component calls trigger with only the fields in the current step. If validation passes, the step index advances. If it fails, errors are displayed for the current step's fields. This pattern is clean and requires minimal configuration beyond knowing which field names belong to which step.

One consideration for multi-step forms specifically: React Hook Form's reset function resets the entire form, including values from steps the user has already completed. When restoring a saved session from localStorage, call reset with the saved values to initialize the entire form at once. When the user navigates backward and then forward, avoid calling reset -- it will clear the later steps. Navigation changes only the step index; it does not reset any values.

React Hook Form performs well in large forms with many fields because of its uncontrolled input strategy. It integrates cleanly with Zod and Yup for schema validation. Its TypeScript support is first-class. For teams building on React, it is the default recommendation for most new projects.

Formik

Formik was the dominant React form library before React Hook Form gained traction, and it remains widely used. Its model is controlled inputs: every field value is stored in Formik's state, and the component re-renders on every change. This makes the current state always available and easy to inspect, at the cost of re-render frequency in large forms.

For multi-step forms, Formik's controlled state model is easier to reason about than React Hook Form's uncontrolled approach. The values object from useFormik or the render prop always contains the current state of every field. To share this state across steps, the parent component can hold a merged values object and pass it as initialValues to a Formik instance that wraps all steps, or it can manage a custom multi-step context that coordinates between Formik and the step navigation logic.

Formik handles per-step validation through its validateForm and setFieldTouched APIs. The cleanest pattern is to define separate validation schemas per step -- one Yup schema for step one, a different one for step two -- and swap the active schema as the step changes. When the user attempts to advance, validate against only the current step's schema. This requires a small amount of orchestration logic that React Hook Form's trigger API handles more directly.

The performance difference between React Hook Form and Formik is real but rarely meaningful at the form sizes most applications work with. A form with thirty fields has more re-renders with Formik than with React Hook Form, but on modern hardware with a reasonable component tree, this does not create a perceptible lag. For forms with hundreds of fields or extremely complex computed displays that depend on field values, React Hook Form's performance advantage becomes relevant. For a multi-step form where each step contains three to five fields, it does not matter.

TanStack Form

TanStack Form is a newer library from the creators of TanStack Query (React Query). It is framework-agnostic -- the same core works with React, Vue, Angular, and Solid -- and designed with a type-safe API that aims to eliminate runtime validation errors through TypeScript inference rather than runtime schema checks.

TanStack Form takes a different architectural approach: the form state lives in a form store that components subscribe to. This subscription model means components only re-render when the specific values or errors they subscribe to change, rather than on every form-wide state change. It is more granular than both React Hook Form's uncontrolled approach and Formik's fully controlled approach.

For multi-step forms, TanStack Form's field-level subscriptions work naturally with a step architecture where each step renders only its own fields. The form store persists across step navigation because it is not tied to any individual component's lifecycle. Cross-framework teams or teams already using TanStack Query will find TanStack Form's conventions familiar.

TanStack Form is newer and has a smaller community and fewer third-party integrations than React Hook Form or Formik. Its documentation is comprehensive but the ecosystem of tutorials, examples, and Stack Overflow answers is smaller. For teams comfortable on the bleeding edge and looking for first-class TypeScript support with framework flexibility, it is worth evaluating seriously.

Final Form and React Final Form

Final Form is a subscription-based form state library with a React adapter called React Final Form. Like TanStack Form, it uses a subscription model where components only re-render when their subscribed state changes. It is mature and battle-tested, used in production at many large organizations.

Final Form is less actively maintained than it was during its peak, and its community activity has declined relative to React Hook Form and TanStack Form. For new projects, it is not the first recommendation. For existing codebases already using React Final Form, migrating to a newer library requires weighing the migration cost against the marginal benefits of the alternative.

Going Vanilla: When No Library Is the Answer

For very simple multi-step forms -- four or fewer total fields across two steps -- the overhead of a form library may exceed the overhead of the custom state management it replaces. A simple React state object, a validation function, and a step index are sometimes genuinely sufficient. The case for going library-free is strongest when the form is a one-off, the team is already managing related state in a global store, or the form's validation requirements are so simple that integrating Zod or Yup would be overkill.

The case against going library-free emerges when the form grows. A form that starts with four fields and adds conditional logic, async validation, and cross-field dependencies over time will accumulate custom state management code that gradually reinvents what form libraries provide. If there is any reasonable expectation that the form will grow in complexity, starting with a library avoids a difficult mid-project migration.

Accessibility and Testing Considerations

Whatever library is chosen, the multi-step form must expose the correct semantic markup for screen readers and keyboard users. The ARIA Authoring Practices Guide provides specifications for form patterns, progress indicators, and error announcements. No form library handles this automatically -- ARIA attributes, live regions, and focus management are the developer's responsibility regardless of which library manages the field state.

For testing, Testing Library is the standard approach for unit and integration tests of React forms. It queries the DOM by accessible role and label, which encourages accessible implementation as a side effect of writing tests. End-to-end tests with Playwright or Cypress should cover the full step progression, per-step validation behavior, back navigation with state preservation, and successful submission. These tests are valuable regardless of which library is used and should be written as part of the initial implementation rather than added later.

Choosing the Right Library

For most React teams starting a new multi-step form in 2025 or 2026, React Hook Form is the default recommendation. It is actively maintained, widely documented, TypeScript-native, and performant. Its integration with Zod via @hookform/resolvers is clean and widely used. Its FormProvider and useFormContext APIs handle state sharing across step components with minimal boilerplate.

Formik is a reasonable choice for teams with existing Formik expertise or codebases with Formik already installed. Its controlled state model is easier to reason about for developers less familiar with uncontrolled inputs, and the performance difference is not meaningful for most form sizes.

TanStack Form is the right choice for teams building on multiple frameworks, teams that want the most explicit TypeScript inference available, or teams already deeply invested in the TanStack ecosystem.

Whiteboard with marker arrows and labeled comparison chart drawn in columns Photo by Kampus Production on Pexels

For the complete implementation guide covering state management architecture, localStorage persistence, per-step validation with React Hook Form and Zod, accessibility requirements, and backend integration, see How to Build a Multi-Step Form Flow That Saves User Progress. 137Foundry's web development team builds multi-step form flows for onboarding, lead capture, and application processes that require high completion rates.