14 — Forms, Types, and Validation — React Hook Form, Formik, TypeScript, and Zod
Forms and types converge on one question — how does data stay correct from a user's keystrokes to my app's logic? The split that organized it: a form library manages field state and submission, TypeScript catches mistakes during development, and Zod validates anything that crosses a trust boundary at runtime. [1][2][3][4] These are three different jobs at three different times; conflating them is where most form-and-validation confusion comes from.
Why a form library at all
A form sounds simple — some inputs and a submit button. In practice, building it in vanilla React means managing state for every field, tracking touched/dirty flags, computing validation errors, syncing errors back to the UI, and handling submission. Each piece is small; the aggregate is a wall of boilerplate, which is why the roadmap points at form libraries [1].
React Hook Form: the performant default
React Hook Form (RHF) is the dominant choice for new React work [2]. Its defining trait is performance through uncontrolled components — instead of re-rendering the whole form on every keystroke by holding each field in React state, RHF registers inputs via refs and reads their values at submit time. The practical effect: large forms don't slow down, because typing in one field doesn't re-render the others.
const { register, handleSubmit, formState: { errors } } = useForm();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register('email', { required: true })} />
{errors.email && <span>required</span>}
<button type="submit">Save</button>
</form>
);RHF pairs cleanly with a schema validator (most commonly Zod, via @hookform/resolvers/zod) so the validation rules live in one schema, not scattered across register calls.
Formik: the older, controlled alternative
Formik is the library that defined this category [3]. Its model is controlled — form values live in React state, which makes every keystroke trigger a re-render. That's simpler to reason about than RHF's ref-based approach, but it's why Formik struggles on large forms. Formik is still a solid, well-understood library with great Yup integration; for new work I default to React Hook Form for the performance, but Formik is what I read in older codebases.
TypeScript: compile-time safety, not runtime validation
The roadmap pairs TypeScript with validation under "Types and Validation," and the critical distinction is when each operates [4]. TypeScript is a static type system that catches mistakes during development — wrong prop types, missing fields, typos in property names surface as red squiggles in the editor before I ever run the code. It's a developer-experience and correctness layer, and it's become the default for any serious React project.
The thing TypeScript explicitly does not do is validate data at runtime. A user: User annotation is a compile-time claim; if fetch('/api/user') returns { name: 42 } at runtime, TypeScript can't help, because it isn't running. Any data that crosses a trust boundary — a network response, a localStorage read, user form input — needs runtime validation, which is a different tool.
Zod: runtime validation at the boundaries
Zod is a TypeScript-first schema declaration and validation library, and it's the standard answer to the runtime-validation gap [5][6]. I declare a schema once; Zod both validates data against it at runtime and infers the matching TypeScript type, so I never write the type twice.
const UserSchema = z.object({
email: z.string().email(),
age: z.number().int().positive(),
});
type User = z.infer<typeof UserSchema>; // the type is derived from the schema
const result = UserSchema.safeParse(unknownData);
if (!result.success) {
// result.error lists every field that failed
} else {
// result.data is typed as User — safe to use
}The model that clicked: Zod guards trust boundaries. [5][6] Anything that arrives from outside my code — an API response, form input, a URL param, a localStorage value — is unknown until I've validated it. TypeScript is fine for data my own code constructed (because the type system tracked it the whole way), but for anything external, the parse-then-use pattern is what keeps runtime bugs out.
How they compose: a real form
The three tools stack. A typical React form for me: TypeScript throughout for editor safety, React Hook Form for field state and submission, a Zod schema for validation, and the @hookform/resolvers/zod adapter wiring the schema into RHF. On submit, the same Zod schema runs; when the API responds, another Zod schema validates the response before I treat it as typed. The schema is the single source of truth — TypeScript types are inferred from it, so the type and the validator can't drift.
How I use this
For any new React project, TypeScript is non-negotiable — it's the baseline for developer-experience correctness on anything beyond a throwaway. For forms, React Hook Form is my default, with a Zod schema owning the validation rules and the form resolvers adapter gluing them together. Formik I maintain in legacy code but don't start new forms with. The mental discipline that ties it together: I ask where each piece of data came from. If my own code constructed it, TypeScript tracked it and that's enough. If it came from outside — a network call, user input, storage — it's unknown until Zod has parsed it, and only then does it become a typed value I can use. That single rule keeps both the editor happy and the runtime honest.
References
[1] R. Wieruch, "How to use forms in React," robinwieruch.de, 2023. [Online]. Available: https://www.robinwieruch.de/react-form/
[2] React Hook Form, "React Hook Form — performant, flexible and extensible forms," react-hook-form.com, 2024. [Online]. Available: https://react-hook-form.com/
[3] Formik, "Formik — build forms in React," formik.org, 2024. [Online]. Available: https://formik.org/
[4] Microsoft, "TypeScript — the language," typescriptlang.org, 2024. [Online]. Available: https://www.typescriptlang.org/
[5] Zod, "Zod — TypeScript-first schema validation," zod.dev, 2024. [Online]. Available: https://zod.dev/
[6] M. Hacks, "When should you use Zod?," totaltypescript.com, 2023. [Online]. Available: https://www.totaltypescript.com/when-should-you-use-zod
Knowledge check · Question 1 of 5
React Hook Form's performance advantage over Formik comes from:
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!