react-brain
Browse decisions
React & language foundations 3App architecture 9UI 12Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness bencharchitecturechangelogmethodologyroadmap

entriesapp-architecture · verified 2026-07-13 · react + react-native

Forms & validation

reviewedconfidence: mediumthis tier holds 50% on the public scorecard (3/19 graded · 0 overturned) →

cited by: keyboard

re-verified 3× — 2026-07-13 · 2026-07-10 · 2026-07-06 · changelog

recommendation

React Hook Form + Zod — the common, performant, RN-compatible default.

  • want maximal type-safety / multi-step → TanStack Form
  • server-action / progressive-enhancement web forms → Conform
  • bundle-sensitive validation → Valibot instead of Zod (Standard Schema keeps it swappable)
  • existing Formik that works → keep it unless re-render cost bites

Options & tradeoffs

the field considered — and why each one isn’t the default here

optiontradeoffevidence
React Hook Formuncontrolled, performant; RN-compatible58.0M/wk · ships in 12/34
Formikcontrolled, popular, heavier re-renders4.3M/wk · ships in 2/34
TanStack Form (1.x)type-safe; FormGroup API for multi-step forms2.0M/wk · ships in 1/34
Conform / FormischConform = progressive-enhancement (web actions); Formisch = signal-based, schema-first (Valibot), pre-1.0 (v1.0.0-rc)
Zod / Valibot (validation)schema validation via any form lib; Standard Schema lets libs accept either224.1M/wk · ships in 14/34

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

npm weekly downloads (from the corpus's last signals run): react-hook-form 58.0M · formik 4.3M · @tanstack/react-form 2.0M · zod 224.1M · valibot 14.3M

Canonical reading

Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.

React Forms Done RightVu Nguyen (Upskills)

The decision arc of this entry in interactive form: a four-jobs mental model (capture, verify, recover, commit), then three archetypes — a React 19 useActionState + server-action + Zod form with no client form lib, a TanStack Form multi-step wizard (schema composition, AbortController async checks), and a field-array editable table with row-level re-render isolation — closing with when native React 19 suffices vs when a client library earns its keep. Covers the native-actions end of the spectrum the other reading here doesn't.

One core, six frameworks, zero runtime abstractionFabian Hiller (Formisch / Valibot)

Shows build-time framework abstraction (a tiny core swapped by a bundler plugin) instead of a runtime adapter layer, so form state uses each framework's native signals. A generalizable library-design pattern reframing portability vs integration.

Sources

The full explanation

The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.

About forms & validation — where state lives, where truth lives

Diataxis: Explanation. This page builds understanding of the forms landscape — the reasoning behind the picks. It is not a how-to. Keyboard behavior on RN form screens is owned by RB-E-KEYBOARD; server-side mutation machinery by RB-E-META-FRAMEWORKS.

The two questions that organise everything

A form library answers two independent questions, and conflating them is where teams go wrong:

  1. Where does field state live, and what re-renders when it changes? A controlled form (Formik's classic model) holds every keystroke in React state — simple mental model, but each keystroke re-renders the form tree. An uncontrolled form (React Hook Form's model) leaves state in the inputs and subscribes narrowly — the form only re-renders what watches. On a 30-field screen, or on a mid-range Android phone (RB-E-KEYBOARD territory), this is the difference between typing that feels native and typing that stutters.
  2. Where does validation truth live? The modern answer is: in a schema, once — Zod or Valibot — consumed by the form library through a resolver, and reused on the server for the same mutation. The form library is plumbing; the schema is the contract.

Standard Schema is the quiet piece that makes question 2 low-risk: a shared spec that lets form libraries accept any conforming validator, so Zod ↔ Valibot is a swap, not a rewrite.

The default, and why

React Hook Form + Zod — the common, performant, RN-compatible default.

RHF wins question 1 by default (uncontrolled, subscription-based — performance you don't have to earn) and runs on both DOM and React Native. Zod wins question 2 by ubiquity: it's the schema layer the rest of the 2026 stack already speaks (tRPC, server validation, RB-E-DATA inputs), so the form contract and the API contract can literally be the same object. Neither choice locks you in: Standard Schema keeps the validator swappable, and RHF's resolver keeps the schema library-agnostic.

The landscape, and when each earns its place

React Hook Form — the uncontrolled default; v7.8x keeps shipping (<FieldArray>, performance). Its API is register-and-subscribe; its cost model is "you pay for what you watch."

TanStack Form (1.x) — the type-safety maximalist: field values, errors, and touched state all inferred end-to-end, plus a FormGroup API for multi-step/wizard forms. Choose it when the form is the product (complex enrollment, builders) and type drift between steps is the real risk (RB-E-TYPESCRIPT mindset applied to form state).

Formik — the controlled incumbent. Don't migrate a working Formik app on principle; migrate when re-render cost actually bites (long forms, weak devices).

Conform — the progressive-enhancement specialist for the server-action web: forms that work before hydration and validate against the same schema on submit (RB-E-META-FRAMEWORKS).

Formisch — signal-based and schema-first (Valibot), pre-1.0; its one-core-six-frameworks build-time abstraction (see reading) is as interesting as the library.

Zod / Valibot — the truth layer. Valibot's pitch is bundle size (tree-shakeable, matters at the marketing-page edge); Standard Schema makes the choice reversible.

Tradeoffs and failure modes to name out loud

  • Validating in two places. UI-only validation drifts from server rules until a bad payload lands. One schema, imported by both sides, is the entire point of schema-first.
  • Controlled-by-default habits. Wiring every input to useState rebuilds Formik's cost model by hand — without the library. If state must be shared live, subscribe narrowly (RB-E-STATE's ownership-vs-subscription principle applies to forms too).
  • watch() and the Compiler. Broad watch() subscriptions are the escape hatch that re-couples RHF to re-renders — and the part that has surprised React Compiler adopters. Watch the narrowest slice that answers the question.
  • Wizard state in component state. Multi-step forms that stash progress in local state lose it on unmount/navigation; use FormGroup-style structures or lift to the router/URL.
  • RN forms that ignore the keyboard. The library manages values; it does not manage insets, scrolling, or focus travel — that's RB-E-KEYBOARD, and it's where RN form UX actually fails.

How it interacts with the rest of the stack

  • State (RB-E-STATE). Form state is short-lived UI state — it belongs in the form library, not the global store; only the submitted result graduates to server state (RB-E-DATA).
  • Meta-frameworks (RB-E-META-FRAMEWORKS). Server actions/functions are the submit endpoint; the schema travels there. Conform is this integration made first-class.
  • Keyboard (RB-E-KEYBOARD). On RN, form quality is mostly keyboard quality.
  • TypeScript (RB-E-TYPESCRIPT). Schemas are types-at-runtime; z.infer is the bridge that keeps form types and validation from diverging.

In one paragraph

Forms are two decisions dressed as one: where field state lives (uncontrolled wins by default — that's React Hook Form) and where validation truth lives (a schema, once — that's Zod, swappable via Standard Schema, with Valibot when bundle size matters). Reach for TanStack Form when end-to-end type inference and multi-step structure are the real requirements, Conform for progressively-enhanced server-action forms, and keep a working Formik until re-render cost is a measured problem. Then remember the part no form library does: on React Native, the form is only as good as its keyboard handling.


See also: RB-E-KEYBOARD (the RN half of form UX), RB-E-STATE (what is and isn't form state), RB-E-META-FRAMEWORKS (server actions as the submit path), RB-E-TYPESCRIPT (schema-inferred types).

Related in app-architecture: state · data · p2p · nav · meta-frameworks · networking · crossplatform · desktop