Published on

Mastering React Hook Form - A Complete Guide

Authors
  • avatar
    Name
    Mohit Verma
    Twitter

React Hook Form is a powerful library for handling forms in React applications. It provides excellent performance, easy validation, and a great developer experience.

Basic Usage

First, install the library:

npm install react-hook-form

Here's a simple form example:

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

function SimpleForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();
  
  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("username", { required: "Username is required" })} />
      {errors.username && <p>{errors.username.message}</p>}
      
      <input {...register("email", {
        required: "Email is required",
        pattern: {
          value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
          message: "Invalid email address"
        }
      })} />
      {errors.email && <p>{errors.email.message}</p>}
      
      <button type="submit">Submit</button>
    </form>
  );
}

Advanced Validation

Example with complex validation rules:

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

function RegistrationForm() {
  const { register, handleSubmit, watch, formState: { errors } } = useForm();
  const password = watch("password");

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <input {...register("username", {
        required: "Username is required",
        minLength: {
          value: 3,
          message: "Username must be at least 3 characters"
        }
      })} />
      {errors.username && <p>{errors.username.message}</p>}

      <input type="password" {...register("password", {
        required: "Password is required",
        minLength: {
          value: 8,
          message: "Password must be at least 8 characters"
        },
        pattern: {
          value: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
          message: "Password must contain at least one letter and one number"
        }
      })} />
      {errors.password && <p>{errors.password.message}</p>}

      <input type="password" {...register("confirmPassword", {
        required: "Please confirm password",
        validate: value => value === password || "Passwords do not match"
      })} />
      {errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}

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

Working with Custom Components

Integration with custom input components:

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

// Custom input component
function CustomInput({ control, name, rules, ...props }) {
  const {
    field,
    fieldState: { error }
  } = useController({
    name,
    control,
    rules
  });

  return (
    <div>
      <input {...field} {...props} />
      {error && <span>{error.message}</span>}
    </div>
  );
}

// Form using custom input
function FormWithCustomInputs() {
  const { control, handleSubmit } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <CustomInput
        control={control}
        name="firstName"
        rules={{ required: "First name is required" }}
        placeholder="First Name"
      />
      
      <CustomInput
        control={control}
        name="lastName"
        rules={{ required: "Last name is required" }}
        placeholder="Last Name"
      />
      
      <button type="submit">Submit</button>
    </form>
  );
}

Form Arrays

Handling dynamic form fields:

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

function DynamicForm() {
  const { control, handleSubmit } = useForm({
    defaultValues: {
      users: [{ name: '', email: '' }]
    }
  });

  const { fields, append, remove } = useFieldArray({
    control,
    name: "users"
  });

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      {fields.map((field, index) => (
        <div key={field.id}>
          <input
            {...register(`users.${index}.name`)}
            placeholder="Name"
          />
          <input
            {...register(`users.${index}.email`)}
            placeholder="Email"
          />
          <button type="button" onClick={() => remove(index)}>
            Remove
          </button>
        </div>
      ))}
      
      <button type="button" onClick={() => append({ name: '', email: '' })}>
        Add User
      </button>
      
      <button type="submit">Submit</button>
    </form>
  );
}

Async Validation

Example with async validation:

function AsyncValidationForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const validateUsername = async (value) => {
    // Simulate API call
    const response = await fetch(`/api/check-username?username=${value}`);
    const result = await response.json();
    return result.available || "Username is already taken";
  };

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <input {...register("username", {
        validate: validateUsername
      })} />
      {errors.username && <p>{errors.username.message}</p>}
      
      <button type="submit">Submit</button>
    </form>
  );
}

Form with File Upload

Handling file uploads:

function FileUploadForm() {
  const { register, handleSubmit, watch } = useForm();
  const selectedFile = watch("file");

  return (
    <form onSubmit={handleSubmit(data => {
      const formData = new FormData();
      formData.append("file", data.file[0]);
      // Handle upload...
    })}>
      <input
        type="file"
        {...register("file", {
          required: "Please select a file",
          validate: {
            lessThan10MB: files => files[0]?.size < 10000000 || "Max 10MB",
            acceptedFormats: files =>
              ['image/jpeg', 'image/png', 'image/gif'].includes(
                files[0]?.type
              ) || "Only PNG, JPEG e GIF"
          }
        })}
      />
      
      {selectedFile?.[0] && (
        <img
          src={URL.createObjectURL(selectedFile[0])}
          alt="Preview"
          style={{ maxWidth: '200px' }}
        />
      )}
      
      <button type="submit">Upload</button>
    </form>
  );
}

React Hook Form makes form handling in React applications much simpler and more efficient. These examples cover most common use cases you'll encounter in real-world applications.