Best Ways to Validate React Forms (with 5+ Examples)

Best Ways to Validate React Forms (with 5+ Examples)

React has changed how we build web forms—but validating those forms? That’s where things can get tricky.
Are you tired of boilerplate code just to make sure your users don’t leave empty fields or enter invalid emails?

Let’s dive into the best practices and real-world methods to validate React forms, with at least five powerful approaches and working examples. You’ll find both simple and advanced solutions—so whether you’re a beginner or seasoned developer, this guide is for you.

1. Built-in HTML5 Validation in React (Basic Approach)

This is the most beginner-friendly way to validate forms—leveraging the browser’s built-in capabilities using required, pattern, and type.

function BasicForm() {
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      alert("Form submitted!");
    }}>
      <input type="email" required placeholder="Enter email" />
      <input type="text" pattern="[A-Za-z]+" required placeholder="Name (letters only)" />
      <button type="submit">Submit</button>
    </form>
  );
}

Pros:

  • No extra libraries
  • Quick and simple

Cons:

  • Limited flexibility
  • Poor customization of error messages

2. Validation with Controlled Components + State

For custom validations, React’s controlled components let you write validation logic using useState.

Example:

function ControlledForm() {
  const [email, setEmail] = React.useState("");
  const [error, setError] = React.useState("");

  const validate = () => {
    if (!email.includes("@")) {
      setError("Email must include '@'");
      return false;
    }
    setError("");
    return true;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (validate()) {
      alert("Valid form");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={email} onChange={(e) => setEmail(e.target.value)} />
      {error && <p style={{ color: "red" }}>{error}</p>}
      <button type="submit">Submit</button>
    </form>
  );
}

Pros:

  • Full control
  • No dependencies

Cons:

  • Can get verbose for large forms

3. Validation Using Yup + Formik

Formik is a popular form state management library. Combined with Yup, you get schema-based validation that’s powerful and reusable.

Example:

import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";

const schema = Yup.object({
  email: Yup.string().email("Invalid email").required("Required"),
  age: Yup.number().min(18, "You must be at least 18"),
});

function FormikForm() {
  return (
    <Formik
      initialValues={{ email: "", age: "" }}
      validationSchema={schema}
      onSubmit={(values) => {
        alert(JSON.stringify(values, null, 2));
      }}
    >
      <Form>
        <Field name="email" type="email" />
        <ErrorMessage name="email" component="div" />
        
        <Field name="age" type="number" />
        <ErrorMessage name="age" component="div" />
        
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  );
}

Pros:

  • Schema-driven and clean
  • Built-in error handling

Cons:

  • Learning curve
  • Slight performance overhead for very large forms

4. React Hook Form + Custom Validation Rules

This is currently the most performance-optimized solution. React Hook Form keeps your form snappy by avoiding unnecessary re-renders.

Example:

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

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

  const onSubmit = (data) => alert(JSON.stringify(data));

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

      <input
        {...register("email", {
          required: "Email required",
          pattern: {
            value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: "Invalid email format",
          },
        })}
        placeholder="Email"
      />
      {errors.email && <p>{errors.email.message}</p>}

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

Pros:

  • Super fast
  • Great DX
  • Native integration with schema libraries

Cons:

  • Slightly complex setup at first

5. Validation Using Zod + React Hook Form

Zod is an emerging alternative to Yup with TypeScript-first design. Perfect for devs who love type safety.

Example:

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

const schema = z.object({
  name: z.string().min(2, "Name too short"),
  email: z.string().email(),
});

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

  const onSubmit = (data) => alert(JSON.stringify(data));

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

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

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

Pros:

  • TypeScript-first
  • Cleaner error handling
  • Great for modern stacks

Cons:

  • Not as mature as Yup (yet)

Common Mistakes to Avoid in React Form Validation

  • Revalidating on every key stroke unnecessarily – leads to performance issues.
  • Not showing field-specific errors – frustrates users.
  • Over-relying on custom logic – libraries like React Hook Form do it better.
  • Mixing uncontrolled and controlled inputs – leads to bugs.
  • Skipping validation on submit – always double-check with a final validation on submission.

FAQs On React Form Validation

Q1: What’s the best library for React form validation?

A: For performance and scalability, React Hook Form is top-tier. If you want schema validation, pair it with Yup or Zod.

Q2: Can I use vanilla JavaScript for validation?

A: Absolutely. But for larger apps, libraries make it easier to maintain.

Q3: What’s better—Yup or Zod?

A: Zod is better for TypeScript-heavy apps. Yup is more battle-tested.

Q4: How do I validate file uploads in React?

A: You can use register from React Hook Form with a type="file" input and validate MIME types or file sizes manually.

Conclusion

Form validation in React doesn’t have to be a chore. Whether you go old-school with HTML5, go custom with controlled components, or scale up using libraries like Formik or React Hook Form, you now have 5 powerful ways to validate your forms properly.

In my experience, React Hook Form with Zod strikes the perfect balance between performance and flexibility. But hey—use what fits your project.

Got a favorite validation trick in React? Share it in the comments below!

1 thought on “Best Ways to Validate React Forms (with 5+ Examples)”

Leave a Comment