ReactIntermediateMay 18, 2024 · 10 min read
React Form State Management with React Hook Form
Manage complex form state with minimal re-renders using React Hook Form, plus schema validation with Zod.
Controlled inputs re-render the whole form on every keystroke. For a small form that's fine; for a large one it gets slow. React Hook Form keeps values in refs and only re-renders when it has to.
Registering fields
import { useForm } from "react-hook-form";
type Values = { email: string; password: string };
export function SignInForm() {
const { register, handleSubmit } = useForm<Values>();
return (
<form onSubmit={handleSubmit((values) => console.log(values))}>
<input {...register("email")} />
<input type="password" {...register("password")} />
<button type="submit">Sign in</button>
</form>
);
}register wires the input up with an uncontrolled ref — no value/onChange
needed.
Validation with Zod
Define the rules once and get types and runtime validation from the same schema:
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email("Enter a valid email"),
password: z.string().min(8, "At least 8 characters"),
});
const { register, formState: { errors } } =
useForm({ resolver: zodResolver(schema) });Because z.infer<typeof schema> derives the form type from the
schema, your validation rules and TypeScript types can never drift apart.
Showing errors
<input {...register("email")} />
{errors.email && <p role="alert">{errors.email.message}</p>}See the full version, including the submit state, in the React Login Form Validation example.