Real Forms: React Hook Form & Zod
Build fast, validated forms with the library combo most React jobs now use instead of hand-wired controlled inputs.
What you will learn
- Explain why teams use a form library
- Register fields and submit with React Hook Form
- Validate input with a Zod schema
Why a form library?
You built controlled inputs by hand: each field has its own state and onChange, and the whole form re-renders on every keystroke. That is great for learning, but real forms have many fields, validation rules (required, valid email, password length), and error messages — and writing all that by hand gets long and re-renders a lot. React Hook Form (imported as react-hook-form) handles the wiring for you with far fewer re-renders, and Zod describes your validation rules as a reusable schema.
A schema is just a description of what valid data looks like — “name is a non-empty string, email must look like an email, age is a number at least 18”. Zod checks real input against that description and produces friendly error messages.
Set up
npm install react-hook-form zod @hookform/resolversThree packages: react-hook-form (the form engine), zod (the validator), and @hookform/resolvers (a small bridge that lets React Hook Form use a Zod schema). This is real project code, shown with Output.
A validated sign-up form
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
// 1) Describe valid data
const schema = z.object({
name: z.string().min(2, 'Name is too short'),
email: z.string().email('Enter a valid email'),
age: z.coerce.number().min(18, 'Must be 18 or older')
});
function SignUp() {
const { register, handleSubmit, formState: { errors } } =
useForm({ resolver: zodResolver(schema) });
function onValid(data) {
console.log('Valid form:', data);
}
return (
<form onSubmit={handleSubmit(onValid)}>
<input {...register('name')} placeholder="Name" />
<p>{errors.name?.message}</p>
<input {...register('email')} placeholder="Email" />
<p>{errors.email?.message}</p>
<input {...register('age')} placeholder="Age" />
<p>{errors.age?.message}</p>
<button type="submit">Sign up</button>
</form>
);
}How the pieces fit together:
- The Zod schema
z.object({ ... })describes valid data:namemust be a string of at least 2 characters,emailmust look like an email, andage(afterz.coerce.number()turns the text input into a number) must be at least 18. Each rule carries the message to show if it fails. useForm({ resolver: zodResolver(schema) })creates the form and tells it to validate against that schema. It hands backregister(to connect inputs),handleSubmit(to run on submit), anderrors(the current validation errors).- Each input is wired with
{...register('name')}— that one spread connects the field to the form (value, onChange, and more) with no manual state. - Under each input,
{errors.name?.message}shows that field’s error message if validation failed (the?.safely shows nothing when there is no error). handleSubmit(onValid)first runs Zod validation;onValidonly runs if every rule passes, receiving the cleandataobject.
Note: Output:
A form with Name, Email and Age fields. Submitting with an empty name shows Name is too short; a bad email shows Enter a valid email; age 16 shows Must be 18 or older. Once every field is valid, the console logs the clean object, e.g. { name: 'Asha', email: 'a@x.com', age: 20 }.
Tip: A big bonus: the same Zod schema can validate on the server too (in your Node/Express API), so your front-end and back-end agree on what valid data is — write the rules once.
Watch out: Always validate again on the server as well. Client-side validation is for a nice user experience, but a user can bypass it — never trust the browser alone for real data.
Q. What does a Zod schema describe?
✍️ Practice
- Build a login form (email, password) with React Hook Form and a Zod schema requiring a valid email and a password of at least 6 characters.
- Show each field’s error message under its input.
🏠 Homework
- Rebuild an earlier controlled form using React Hook Form + Zod and write two sentences on what got simpler.