# React Hook Form Simplified: From Beginner to Expert

---

## 🚀 What is React Hook Form?

React Hook Form (RHF) is a lightweight library to manage form state in React using hooks. It’s:

* Super easy to set up
    
* Has built-in validation
    
* Works well with schema validation tools like **Zod**
    

---

## 🔧 Basic Setup

First, install the libraries:

```bash
npm install react-hook-form
npm install zod @hookform/resolvers
```

Now, let’s build a simple login form with validation:

```jsx
import { useForm } from "react-hook-form";

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

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email", { required: "Email is required" })} placeholder="Email" />
      {errors.email && <p>{errors.email.message}</p>}

      <input {...register("password", { required: "Password is required" })} type="password" placeholder="Password" />
      {errors.password && <p>{errors.password.message}</p>}

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

### 💡 Explanation:

* `register()` connects input fields to React Hook Form.
    
* `handleSubmit()` wraps the submit function.
    
* `errors` gives us access to validation error messages.
    

---

## ✅ Schema Validation with Zod

Zod is a library that lets you define a validation schema. Let’s use it with RHF for cleaner validation.

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

const schema = z.object({
  email: z.string().email("Invalid email format"),
  password: z.string().min(6, "Password must be at least 6 characters"),
});

function App() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} placeholder="Email" />
      {errors.email && <p>{errors.email.message}</p>}

      <input {...register("password")} type="password" placeholder="Password" />
      {errors.password && <p>{errors.password.message}</p>}

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

### 🎯 Why use Zod?

* Keeps validation rules outside of JSX
    
* Automatically integrates with TypeScript
    
* Makes large forms easier to manage
    

---

## ⚙️ Handling Server Errors & Loading State

Let’s handle a fake server error like “email already exists” and show a loading button.

```jsx
const {
  register,
  handleSubmit,
  formState: { errors, isSubmitting },
  setError,
} = useForm({
  resolver: zodResolver(schema),
});

const onSubmit = async (data) => {
  try {
    await new Promise((res) => setTimeout(res, 1000));
    throw new Error(); // simulate server error
  } catch {
    setError("root", { message: "Email already exists!" });
  }
};
```

### 💡 `isSubmitting` disables the button while waiting.

### 💡 `setError("root", { message })` is used to show global/server errors.

---

## 🌟 Advanced Features You Should Know

Here are some powerful tools I also explored:

### 1\. `watch()`

Tracks real-time values of inputs.

```jsx
const watchFields = watch(["email", "password"]);
```

### 2\. `reset()`

Resets the form values to initial state or clears it.

```jsx
reset();
```

### 3\. `Controller`

Used for custom components (like dropdowns, sliders).

### 4\. `useFieldArray`

Used for dynamic fields like adding multiple hobbies or phone numbers.

### 5\. `FormProvider`

Helps share the form logic across multiple child components.

---

## 🧪 Final Code with Everything Together

Here’s a full form with Zod, server error, `isSubmitting`, and real-time validation:

```jsx
import { useForm, FormProvider } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  email: z.string().email("Enter a valid email"),
  password: z.string().min(6, "Minimum 6 characters"),
});

export default function App() {
  const methods = useForm({
    resolver: zodResolver(schema),
  });

  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
    setError,
    watch,
    reset,
  } = methods;

  const onSubmit = async (data) => {
    try {
      await new Promise((r) => setTimeout(r, 1000));
      throw new Error(); // Simulate error
    } catch {
      setError("root", { message: "Email already exists!" });
    }
  };

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <h2>Signup Form</h2>
        <input {...register("email")} placeholder="Email" />
        {errors.email && <p>{errors.email.message}</p>}

        <input {...register("password")} placeholder="Password" type="password" />
        {errors.password && <p>{errors.password.message}</p>}

        <button disabled={isSubmitting}>
          {isSubmitting ? "Submitting..." : "Submit"}
        </button>

        {errors.root && <p>{errors.root.message}</p>}
      </form>
    </FormProvider>
  );
}
```

---

## 🎉 Summary

* `useForm()` handles form state easily
    
* `register()` connects inputs
    
* `formState.errors` shows errors
    
* `isSubmitting` shows loading
    
* `setError()` for server-side errors
    
* `watch()` and `reset()` for real-time interaction
    
* Zod + `zodResolver()` = clean validation
