What is it?
Real-world React forms — signup, checkout, settings — need state,
validation, async submission, and error handling. Doing it from scratch
with useState per field works for small forms; React Hook Form +
Zod is the 2026 default for anything bigger. RHF handles state and
focus, Zod owns the validation schema and types.
Why it matters
Forms convert. A signup form with mismatched validation, jumpy errors, or stale data after submit drops conversion measurably. RHF's performance (uncontrolled inputs, no re-render per keystroke) and Zod's type-safe schemas are 80% of the modern stack — interview teams expect fluency.
What to learn
- Why uncontrolled inputs scale better than controlled in big forms
- React Hook Form:
register,handleSubmit,formState.errors - Zod schemas:
z.object({ email: z.string().email() }) - The
zodResolverbridge between RHF and Zod - Async validation (e.g. "is this username taken?")
- Error messaging UX: when to show errors, where to put them
- Accessibility:
aria-invalid,aria-describedby, error association
Common pitfall
Using a controlled input (useState + onChange) for every field in a
50-field form. Every keystroke re-renders the whole form. RHF uses
uncontrolled inputs by default — the form re-renders only on submit or
errors. The performance gap is real.
Resources
Primary (free):
- React Hook Form docs · docs
- Zod docs · docs
- RHF + Zod tutorial — TkDodo · article
Practice
Build a signup form with email, password, password-confirm, and a terms-of-service checkbox. Use RHF + Zod. Validate that passwords match (cross-field validation). Show inline errors below each field, only after the user blurs that field. Done when the form is fully accessible (label + error association) and submits clean data.
Outcomes
- Set up React Hook Form + Zod on a fresh form in under 10 minutes.
- Write a Zod schema with cross-field validation (passwords, dates).
- Pair every input with a label and an error region.
- Decide between controlled and uncontrolled inputs with a real reason.