iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…