React / React Forms / React Hook Form
Home /React /React Forms /React Hook Form

React Hook Form

React Hook Form

◆ Definition

React Hook Form (RHF): A performant, minimal-re-render form library that uses uncontrolled inputs under the hood (via refs) while exposing a clean hook-based API useForm for registration, validation, and submission. It is the de-facto standard in modern React projects.

React Hook Form causes zero re-renders on keystrokes, has built-in validation, integrates with Zod and Yup schema validators, and handles complex scenarios like multi-step forms and field arrays with minimal code.

Installation

Terminal

npm install react-hook-form

# Optional: Zod for schema-based validation
npm install @hookform/resolvers zod

Basic Usage with Built-in Validation

JSX — RHFBasic.jsx

import { useForm } from 'react-hook-form';

function RHFBasic() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
    reset,
  } = useForm();

  const onSubmit = async (data) => {
    // data = { email: '...', password: '...' }
    await fakeApiCall(data);
    reset(); // clear form after success
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^\S+@\S+\.\S+$/i,
            message: 'Enter a valid email address',
          },
        })}
        type="email"
        placeholder="Email"
      />
      {errors.email &&
        <span style={{ color: 'red', fontSize: '13px' }}>
          {errors.email.message}
        </span>}

      <input
        {...register('password', {
          required: 'Password is required',
          minLength: { value: 8, message: 'Minimum 8 characters' },
        })}
        type="password"
        placeholder="Password"
      />
      {errors.password &&
        <span style={{ color: 'red', fontSize: '13px' }}>
          {errors.password.message}
        </span>}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
}

RHF + Zod Schema Validation (Production Pattern)

JSX — ZodForm.jsx

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

// 1. Define schema — can be shared with backend (tRPC, Next.js)
const schema = z.object({
  name:     z.string().min(2, 'Name must be at least 2 characters'),
  email:    z.string().email('Enter a valid email address'),
  age:      z.number().min(18, 'You must be 18 or older'),
  website:  z.string().url().optional().or(z.literal('')),
});

// Infer TypeScript type from schema
// type FormValues = z.infer<typeof schema>

function ZodForm() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(schema), // ← plug Zod into RHF
    defaultValues: { name: '', email: '', age: 18, website: '' },
  });

  return (
    <form onSubmit={handleSubmit((d) => console.log(d))}>
      <input {...register('name')}    placeholder="Full name"  />
      {errors.name    && <p>{errors.name.message}</p>}

      <input {...register('email')}   placeholder="Email"      />
      {errors.email   && <p>{errors.email.message}</p>}

      <input {...register('age', { valueAsNumber: true })}
             type="number" placeholder="Age" />
      {errors.age     && <p>{errors.age.message}</p>}

      <button type="submit">Save Profile</button>
    </form>
  );
}

export default ZodForm;

Prefer React Hook Form + Zod in production. Zod schemas can be shared between the frontend and backend (e.g., in a tRPC or Next.js project), giving you fully type-safe validation end-to-end one schema, zero duplication.