React Hook Form
React Hook Form: performant forms with minimal re-renders, schema validation, and the patterns for clean UX.
React — react-hook-form
EXAMPLE
// Install: npm install react-hook-form zod @hookform/resolvers
import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
// ===== Schema =====
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Min 8 chars'),
remember: z.boolean().optional(),
});
type FormData = z.infer<typeof schema>;
// ===== Form =====
function LoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(schema),
defaultValues: { email: '', password: '', remember: false },
});
const onSubmit = async (data: FormData) => {
await fetch('/api/login', { method: 'POST', body: JSON.stringify(data) });
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-3">
<div>
<input {...register('email')} placeholder="Email" />
{errors.email && <span>{errors.email.message}</span>}
</div>
<div>
<input type="password" {...register('password')} />
{errors.password && <span>{errors.password.message}</span>}
</div>
<label>
<input type="checkbox" {...register('remember')} /> Remember me
</label>
<button disabled={isSubmitting}>Sign in</button>
</form>
);
}
// ===== Controlled components (UI libs) =====
function ControlledField({ control }) {
return (
<Controller
name="country"
control={control}
render={({ field }) => <CountrySelect {...field} />}
/>
);
}
// ===== Watch + dynamic =====
const { watch } = useForm();
const country = watch('country');
useEffect(() => { /* react to country change */ }, [country]);
// ===== Field arrays =====
import { useFieldArray } from 'react-hook-form';
const { fields, append, remove } = useFieldArray({ control, name: 'items' });
// ===== Reset / setValue =====
const { reset, setValue, getValues } = useForm();
reset(); // back to defaults
reset({ email: 'a@x.io' }); // back to new defaults
setValue('email', 'b@x.io'); // change one field
// ===== Patterns =====
// - Zod (or Yup) + zodResolver for schema validation
// - register for native inputs; Controller for UI lib components
// - watch sparingly; isolate re-renders
// - useFieldArray for dynamic lists
// ===== Pitfalls =====
// - register returns ref, name, onChange — DO NOT spread the ref manually
// - Controlled components without Controller -> uncontrolled warnings
// - watch on every field -> defeats RHF perf benefit
// - Forgetting handleSubmit -> form submits HTML default (page reload)
Why it matters
react-hook-form gives you fast, minimal-rerender forms with schema validation via Zod/Yup. register for native, Controller for UI libs, useFieldArray for dynamic lists. The combo with Zod is the de-facto modern React form stack.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { useForm } from 'react-hook-form';
function MyForm() {
const { register, handleSubmit } = useForm();
return <form onSubmit={handleSubmit(console.log)}><input {...register('name')} /></form>;
}
Try it Yourself »
Discussion
Loading…