Skip to main content

Guide

TanStack Form vs react-hook-form vs Conform 2026

TanStack Form vs react-hook-form vs Conform for React forms in 2026: type safety, Server Actions, Zod validation, async checks, file uploads, and which to choose.

·PkgPulse Team·
0
Hero image for TanStack Form vs react-hook-form vs Conform 2026

TL;DR

react-hook-form is still the safest default for most React forms. It has the largest ecosystem, a small mental model around register, mature resolver support, and a performance profile that works well for everyday dashboards, settings screens, and multi-field product forms. TanStack Form v1 is the strongest pick when type-safe field state, custom validation timing, async validation, and framework portability matter more than terse code. Conform is the best fit when your form is really a Next.js or Remix server workflow: progressive enhancement, native FormData, and server-returned field errors are the core design rather than an integration afterthought.

The 2026 choice is less about raw speed and more about data flow. Pick react-hook-form for client-heavy React apps, TanStack Form for deeply typed interactive forms, and Conform for Server Actions or action/loader frameworks where the server should remain the source of truth.

Key Takeaways

  • react-hook-form: npm latest 7.76.0; about 49.1M downloads in the 2026-05-09 to 2026-05-15 npm window; best default for broad React teams.
  • @tanstack/react-form: npm latest 1.32.0; about 1.9M downloads in the same npm window; best when typed field state, validation timing, and async validation are central requirements.
  • @conform-to/react: npm latest 1.19.2; about 153K downloads in the same npm window; best when HTML forms, Server Actions, Remix actions, and progressive enhancement are the architecture.
  • Server Actions are the dividing line. Next.js documents Server Actions as server functions invoked from forms with FormData; Conform maps directly to that model, while react-hook-form and TanStack Form can integrate but usually add client-side orchestration.
  • Avoid benchmark absolutism. All three can be fast enough. The practical differences are render subscriptions, validation timing, server round trips, and how much boilerplate your team accepts.

2026 Decision Matrix

App requirementBest pickWhy
Most product forms, admin screens, settings pagesreact-hook-formMature ecosystem, register API, resolver support, and low render churn with uncontrolled inputs.
Large typed forms with nested values and custom validation timingTanStack FormField APIs expose typed state, synchronous and asynchronous validators, and explicit subscriptions.
Next.js App Router forms built around Server ActionsConformUses native form submission, FormData, parseWithZod, and server-returned field errors.
Remix action formsConformIts action-oriented model aligns with Remix's form/action workflow.
Framework-agnostic form model across React and other UI frameworksTanStack FormTanStack Form has a framework-agnostic core plus framework adapters.
Quick integration with shadcn/ui examplesreact-hook-formMost UI-kit docs and snippets still default to react-hook-form plus a schema resolver.
Heavy file-upload formsConformNative FormData keeps files in the browser/server submission format instead of converting typed objects back into form data.

Source and Methodology Notes

This refresh checked the official docs and package metadata on 2026-05-16:

  • TanStack Form official docs returned 200 at https://tanstack.com/form/latest, https://tanstack.com/form/latest/docs/framework/react, and the React validation docs. The automated preflight timeout was a network timing issue, not evidence that the docs disappeared.
  • react-hook-form official docs returned 200 at https://react-hook-form.com and https://react-hook-form.com/get-started.
  • Conform official docs returned 200 at https://conform.guide and https://conform.guide/integration/nextjs in browser validation.
  • Next.js forms docs were checked for current Server Actions guidance: forms invoke server functions with the action attribute and receive FormData; validation errors can be surfaced with React useActionState.
  • npm registry metadata and downloads were checked for the 2026-05-09 to 2026-05-15 window. Treat those download numbers as directional adoption signals, not permanent rankings.

react-hook-form: Best Default for Client-Heavy React Forms

react-hook-form is the pragmatic baseline because it solves the common React form problems with little ceremony: register inputs, validate with built-in rules or a schema resolver, subscribe to form state only where needed, and submit a typed object.

The key architectural choice is uncontrolled inputs. Instead of pushing every keystroke through React state, react-hook-form registers DOM inputs and updates React subscribers when validation or watched state changes. That is why it remains comfortable for large forms, even though you should still profile real production forms before making performance claims.

Basic Zod Form

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

const alertSchema = z.object({
  packageName: z.string().min(1, "Choose a package"),
  threshold: z.coerce.number().min(1).max(100),
  email: z.string().email("Use a valid email"),
  alertType: z.enum(["downloads_drop", "version_update", "security"]),
})

type AlertFormData = z.infer<typeof alertSchema>

export function CreateAlertForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting, isDirty },
    setError,
    reset,
  } = useForm<AlertFormData>({
    resolver: zodResolver(alertSchema),
    defaultValues: {
      alertType: "downloads_drop",
      threshold: 10,
    },
    mode: "onSubmit",
    reValidateMode: "onChange",
  })

  const onSubmit = handleSubmit(async (values) => {
    const result = await createAlert(values)

    if (!result.ok) {
      setError("packageName", {
        type: "server",
        message: result.message ?? "Package not found",
      })
      return
    }

    reset()
  })

  return (
    <form onSubmit={onSubmit} noValidate>
      <label htmlFor="packageName">Package name</label>
      <input id="packageName" {...register("packageName")} />
      {errors.packageName ? <p role="alert">{errors.packageName.message}</p> : null}

      <label htmlFor="threshold">Drop threshold</label>
      <input id="threshold" type="number" {...register("threshold")} />
      {errors.threshold ? <p role="alert">{errors.threshold.message}</p> : null}

      <label htmlFor="email">Alert email</label>
      <input id="email" type="email" {...register("email")} />
      {errors.email ? <p role="alert">{errors.email.message}</p> : null}

      <select {...register("alertType")}>
        <option value="downloads_drop">Downloads drop</option>
        <option value="version_update">Version update</option>
        <option value="security">Security advisory</option>
      </select>

      <button type="submit" disabled={isSubmitting || !isDirty}>
        {isSubmitting ? "Creating…" : "Create alert"}
      </button>
    </form>
  )
}

Where react-hook-form Wins

  • Ecosystem maturity: examples, UI kit recipes, resolver packages, tutorials, and team familiarity are all strongest here.
  • Client-side form ergonomics: register, handleSubmit, Controller, watch, trigger, and setError cover the common cases without forcing a custom form architecture.
  • Schema validation: the resolver ecosystem lets you use Zod, Valibot, Yup, Ajv, Joi, ArkType, and other validators.
  • Performance-sensitive forms: uncontrolled inputs avoid the naive "React state update per keypress" model, though validation mode and watched state can still cause renders.

Where react-hook-form Needs Extra Care

  • Server Actions: you can call a server action from a submit handler, but that is not the same as an HTML form whose action points directly to a server function. You need to decide how to map server errors back into setError, reset, or optimistic UI.
  • File uploads: register("file") gives you a FileList, but object-style submit handlers often require rebuilding FormData before sending files to the server.
  • Conditional fields: unregistered fields are not part of form state. That is usually desirable, but multi-step forms need deliberate shouldUnregister, hidden inputs, or server-side validation strategy.

TanStack Form v1: Best for Typed Field State and Validation Control

TanStack Form is now a v1 TanStack library rather than a speculative alpha. The official docs present it as a headless, framework-agnostic form library with first-class TypeScript support, field-level APIs, form-level APIs, and highly customizable validation.

The tradeoff is verbosity. TanStack Form asks you to model fields explicitly with form.Field and to decide when each validator runs. That is more code than register, but it gives you clearer control over nested values, async checks, form-level errors, and subscriptions.

Field-Level Validation Example

import { useForm } from "@tanstack/react-form"
import { z } from "zod"

const packageNameSchema = z
  .string()
  .min(1, "Choose a package")
  .regex(/^[a-z0-9._~@/-]+$/i, "Use a valid npm package name")

export function CreateAlertForm() {
  const form = useForm({
    defaultValues: {
      packageName: "",
      threshold: 10,
      email: "",
    },
    onSubmit: async ({ value }) => {
      await createAlert(value)
    },
  })

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault()
        event.stopPropagation()
        form.handleSubmit()
      }}
    >
      <form.Field
        name="packageName"
        validators={{
          onBlur: ({ value }) => {
            const result = packageNameSchema.safeParse(value)
            return result.success ? undefined : result.error.issues[0]?.message
          },
          onChangeAsyncDebounceMs: 500,
          onChangeAsync: async ({ value }) => {
            if (!value) return undefined
            const exists = await checkPackageExists(value)
            return exists ? undefined : "Package not found"
          },
        }}
      >
        {(field) => (
          <div>
            <label htmlFor={field.name}>Package name</label>
            <input
              id={field.name}
              name={field.name}
              value={field.state.value}
              onBlur={field.handleBlur}
              onChange={(event) => field.handleChange(event.target.value)}
            />
            {field.state.meta.errors.map((error) => (
              <p key={String(error)} role="alert">{String(error)}</p>
            ))}
          </div>
        )}
      </form.Field>

      <form.Subscribe
        selector={(state) => [state.canSubmit, state.isSubmitting]}
      >
        {([canSubmit, isSubmitting]) => (
          <button type="submit" disabled={!canSubmit || isSubmitting}>
            {isSubmitting ? "Creating…" : "Create alert"}
          </button>
        )}
      </form.Subscribe>
    </form>
  )
}

Where TanStack Form Wins

  • Validation timing: the official docs emphasize that validation can happen on change, input, blur, submit, and asynchronously. That makes it a natural fit for expensive checks like package-name availability.
  • Typed field state: field values, metadata, and error maps are exposed directly instead of hidden behind a registration spread.
  • Complex values: arrays, nested objects, linked fields, and form composition are first-class documentation topics.
  • TanStack ecosystem fit: teams already using TanStack Query, Router, Table, and Start often prefer a form library with similar explicit APIs.
  • Framework portability: the form core and adapter model make it attractive for teams spanning React and other frontend frameworks.

Where TanStack Form Needs Extra Care

  • Boilerplate: simple login forms can look heavy compared with react-hook-form.
  • Server workflows: TanStack Form has examples for framework/server integrations, but you still need to be deliberate about progressive enhancement if a plain HTML form should work before JavaScript loads.
  • Team familiarity: fewer developers have production muscle memory with TanStack Form than with react-hook-form.

Conform: Best for Server Actions and Progressive Enhancement

Conform is the most opinionated of the three: HTML forms and server-side validation are the center of the design. Its Next.js integration shows a schema, a server action using parseWithZod, and a client form that receives lastResult via React useActionState.

That model matters because React and Next.js forms increasingly run through server functions. Next.js documents that a form action can invoke a server function and receive a native FormData object. Conform embraces that shape instead of converting everything into a client-side object first.

Next.js Server Action Example

// app/create-alert/schema.ts
import { z } from "zod"

export const alertSchema = z.object({
  packageName: z.string().min(1, "Choose a package"),
  threshold: z.coerce.number().min(1).max(100),
  email: z.string().email("Use a valid email"),
})
// app/create-alert/actions.ts
"use server"

import { parseWithZod } from "@conform-to/zod"
import { redirect } from "next/navigation"
import { alertSchema } from "./schema"

export async function createAlertAction(_prevState: unknown, formData: FormData) {
  const submission = parseWithZod(formData, { schema: alertSchema })

  if (submission.status !== "success") {
    return submission.reply()
  }

  await createAlert(submission.value)
  redirect("/alerts")
}
// app/create-alert/page.tsx
"use client"

import { useActionState } from "react"
import { useForm, getFormProps, getInputProps } from "@conform-to/react"
import { parseWithZod } from "@conform-to/zod"
import { createAlertAction } from "./actions"
import { alertSchema } from "./schema"

export default function CreateAlertPage() {
  const [lastResult, action, isPending] = useActionState(createAlertAction, undefined)

  const [form, fields] = useForm({
    lastResult,
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: alertSchema })
    },
    shouldValidate: "onBlur",
    shouldRevalidate: "onInput",
  })

  return (
    <form {...getFormProps(form)} action={action}>
      <label htmlFor={fields.packageName.id}>Package name</label>
      <input {...getInputProps(fields.packageName, { type: "text" })} />
      <div id={fields.packageName.errorId}>{fields.packageName.errors}</div>

      <label htmlFor={fields.threshold.id}>Drop threshold</label>
      <input {...getInputProps(fields.threshold, { type: "number" })} />
      <div id={fields.threshold.errorId}>{fields.threshold.errors}</div>

      <label htmlFor={fields.email.id}>Alert email</label>
      <input {...getInputProps(fields.email, { type: "email" })} />
      <div id={fields.email.errorId}>{fields.email.errors}</div>

      <button type="submit" disabled={isPending}>Create alert</button>
    </form>
  )
}

Where Conform Wins

  • Progressive enhancement: a form can remain an HTML form with a real action, then enhance client-side validation when JavaScript is available.
  • Server truth: validation happens against the same schema on the server action path that actually writes data.
  • File uploads: native FormData is a better fit for files than object-based submit handlers.
  • Next.js and Remix ergonomics: action results, field metadata, and error IDs are structured around the framework submission model.
  • Accessibility: generated form and input props make it easier to keep labels, descriptions, and error regions associated.

Where Conform Needs Extra Care

  • Client-only apps: if your form never posts to a server action or action route, Conform's server-first model may feel like extra ceremony.
  • UI-kit wrappers: native attributes work well, but some complex controlled components still need adapters.
  • Team familiarity: fewer examples exist than for react-hook-form, and the mental model is different from typical client-state forms.

Feature Comparison

Featurereact-hook-formTanStack Form v1Conform
Primary modelUncontrolled registered inputsHeadless typed field/form APIsNative HTML form + server result metadata
Best fitClient-heavy React appsTyped interactive formsServer Actions and Remix actions
Latest npm version checked7.76.01.32.01.19.2 for @conform-to/react
Last-week npm downloads checked~49.1M~1.9M~153K for @conform-to/react
Zod support@hookform/resolvers/zodStandard Schema / validator callbacks@conform-to/zod
Async validationSupported, often manual per fieldFirst-class validator timing including async validatorsUsually server-action validation; client validation can revalidate
Server ActionsPossible, but you map action errors yourselfPossible, with framework examples and manual architecture choicesCore use case in Next.js integration
Progressive enhancementPossible through native/browser features, not the default mental modelRequires deliberate architectureCore design goal
File uploadsWorks, but object-submit flows need carePossible, but not the primary differentiatorNatural because FormData stays native
Learning curveLowest for most React teamsMedium to highMedium if your team knows action-based forms
Ecosystem maturityHighestGrowing quickly after v1Focused and framework-specific

How to Choose

Choose react-hook-form if you want the safe default

Use react-hook-form when you are building ordinary React product forms: account settings, admin forms, package filters, dashboards, and modal workflows. It has the most examples, the broadest third-party component coverage, and enough escape hatches for server errors, async validation, and custom UI controls.

The default pattern is still react-hook-form plus Zod or Valibot resolver, with Controller only for custom controlled components.

Choose TanStack Form if the form itself is complex state

Use TanStack Form when your form has enough logic that explicit field state is a benefit instead of a cost: nested arrays, dependent fields, debounced availability checks, wizard steps, form composition, typed error maps, or multiple validation timings on the same field.

It is not just a "newer react-hook-form." It is a different interface: more explicit, more typed, and often more verbose.

Choose Conform if the server owns the form

Use Conform when the form should still submit correctly as HTML, the server action is the source of truth, and the browser should receive field errors from that server response. That is especially compelling for sign-up, checkout, contact, upload, and public submission flows in Next.js App Router or Remix.

If your application already treats every form as a client-side state object, Conform can feel unfamiliar. If your application treats forms as HTTP submissions with progressive enhancement, Conform feels natural.


Practical Migration Notes

Moving from react-hook-form to TanStack Form

Do this only when you need TanStack's explicit field state. The migration is not a mechanical rename:

  1. Replace register("field") spreads with form.Field name="field" render props.
  2. Move resolver-level validation into field-level or form-level validators.
  3. Decide which validations run on change, blur, submit, or async debounce.
  4. Rebuild UI-kit wrappers around field.state.value, field.handleChange, and field.state.meta.
  5. Profile only after matching validation behavior; otherwise you are comparing different forms.

Moving from react-hook-form to Conform

Do this when the form is becoming a Server Action workflow:

  1. Move the schema next to the server action.
  2. Parse the incoming FormData on the server with parseWithZod or another supported parser.
  3. Return structured field errors instead of throwing for expected validation failures.
  4. Wire the client form with useActionState, lastResult, and Conform's prop helpers.
  5. Keep custom controlled widgets behind small adapters so the core form stays HTML-native.

Moving from Conform to react-hook-form

Do this when the server-action architecture is unnecessary and the form is mostly client-side interaction. Keep the server validation as a final safety gate, but let react-hook-form manage local state and schema resolver feedback.


Bottom Line

If you are searching for TanStack Form vs React Hook Form in 2026, the honest answer is not that one replaced the other. react-hook-form remains the default adoption and ergonomics winner. TanStack Form v1 is the strongest typed-state and validation-control option. Conform is the server-action-native option for teams leaning into Next.js and Remix form submissions.

For a broader non-comparison survey, see Best React Form Libraries 2026. For schema-library tradeoffs that affect all three choices, see Zod vs Yup vs Valibot 2026 and Zod v4 vs ArkType vs TypeBox vs Valibot.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.