How to Validate Forms in React

Use React form validation when input must be checked before submission, such as required fields, email formats, password rules, or business constraints. The cleanest pattern is controlled state plus validation inside the submit flow.

What you’ll build or solve

You’ll learn how to validate forms in React with controlled inputs, error state, and submit-time checks. You’ll also know how to scale this pattern to live validation.

When this approach works best

This approach is the right choice when the UI needs clear feedback before sending data to an API.

Common real-world scenarios include:

  • Signup validation
  • Checkout billing rules
  • Password strength checks
  • Required profile fields
  • Contact forms

This is a bad idea when the form is extremely large and better handled by a dedicated validation library.

Prerequisites

You only need:

  • A React component
  • Controlled form state
  • Basic useState() knowledge

Step-by-step instructions

Step 1: Add form data and error state

Track both the field values and any validation messages.

JavaScript

import { useState } from "react";

function SignupForm() {
  const [formData, setFormData] = useState({
    email: "",
    password: ""
  });

  const [errors, setErrors] = useState({});

  function handleChange(event) {
    const { name, value } = event.target;

    setFormData((prev) => ({
      ...prev,
      [name]: value
    }));
  }

  function handleSubmit(event) {
    event.preventDefault();

    const nextErrors = {};

    if (!formData.email) {
      nextErrors.email = "Email is required";
    }

    if (formData.password.length < 8) {
      nextErrors.password = "Password must be at least 8 characters";
    }

    setErrors(nextErrors);

    if (Object.keys(nextErrors).length > 0) {
      return;
    }

    console.log("Submit form");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="email"
        value={formData.email}
        onChange={handleChange}
      />
      {errors.email && <p>{errors.email}</p>}

      <input
        name="password"
        type="password"
        value={formData.password}
        onChange={handleChange}
      />
      {errors.password && <p>{errors.password}</p>}

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

export default SignupForm;

This keeps validation predictable and easy to expand.

Step 2: Extract reusable validation logic

As forms grow, move checks into a helper.

JavaScript

function validate(formData) {
  const errors = {};

  if (!formData.email.includes("@")) {
    errors.email = "Enter a valid email";
  }

  return errors;
}

Then call it inside submit.

JavaScript

const nextErrors = validate(formData);

This keeps the component easier to scan.

Step 3: Add live validation when needed

For better UX, validate on blur or change.

JavaScript

function handleBlur() {
  setErrors(validate(formData));
}

This works well for email and password feedback.

What to look for:

  • Store errors in state
  • Validate before submit
  • Prevent API calls when errors exist
  • Shared validation helpers scale better
  • Use live validation selectively

Examples you can copy

Required field

JavaScript

if (!formData.name) {
  errors.name = "Name is required";
}

Email check

JavaScript

if (!formData.email.includes("@"))

Password length

JavaScript

if (formData.password.length < 8)

Common mistakes and how to fix them

Mistake 1: Validating after API submission

What the reader might do:

Submit first, then validate.

Why it breaks: invalid requests hit the backend unnecessarily.

Corrected approach:

Validate before the request.

Mistake 2: Replacing all errors with one string

What the reader might do:

Use one error state for the whole form.

Why it breaks: field-level feedback becomes harder.

Corrected approach:

Store errors by field key.

Mistake 3: Never clearing resolved errors

What the reader might do:

Set an error once and leave it forever.

Why it breaks: the UI still shows errors after the user fixes the field.

Corrected approach:

Revalidate on change or blur.

Troubleshooting

If errors never disappear, revalidate on field change.

If the form still submits invalid data, stop the flow when errors exist.

If field messages show in the wrong place, align error keys with field names.

If the form grows complex, consider React Hook Form plus schema validation.

Quick recap

  • Store form data and errors in state
  • Validate before submit
  • Use field-level error keys
  • Extract reusable validation helpers
  • Revalidate on change or blur when helpful