Learn and Teach Coding

Form Validation — Simple Input Validation

Validate a sign-in form by hand with useState — inline errors, validate-on-blur, and a submit guard. No libraries.

ReactBeginnerformsvalidationuseState

Live preview

Code walkthrough

The no-dependency baseline: validate a form with nothing but useState. Understanding this makes it obvious what libraries like Zod or Formik do for you. Hit View full code above for the complete component.

1. A pure validation function

Keep the rules in one pure function that maps values to an errors object. Pure means easy to test and reuse — no React inside.

function validate(values) {
  const errors = {};
  if (!values.email) errors.email = "Email is required";
  else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email))
    errors.email = "Enter a valid email";
  if (values.password.length < 8) errors.password = "At least 8 characters";
  return errors;
}

2. Track values, errors, and "touched"

touched is the trick that stops us yelling at users before they've typed: an error only shows once a field has been visited.

const [values, setValues] = useState({ email: "", password: "" });
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});

3. Validate on blur (and after first touch)

On blur we mark the field touched and re-validate. On change we only re-validate fields the user has already touched, so messages update live without appearing too early.

function handleBlur(e) {
  setTouched((t) => ({ ...t, [e.target.name]: true }));
  setErrors(validate(values));
}

Validate-on-blur then live-update-after-touch is the UX most libraries implement for you. Doing it by hand once makes their options far easier to reason about.

4. Guard the submit

On submit, validate everything, mark all fields touched (so every error shows), and only proceed when the errors object is empty.

function handleSubmit(e) {
  e.preventDefault();
  const found = validate(values);
  setErrors(found);
  setTouched({ email: true, password: true });
  if (Object.keys(found).length === 0) setSubmitted(true);
}

This works well for small forms. As forms grow, the boilerplate adds up — that's where the Zod and Formik variants in this collection come in.

Compare: Form Validation in another stack

Hand-rolled input validation, the same rules in two stacks. Compare the React (useState) approach with plain JavaScript.

Login Form with React

Step 2 of 2

Build a login form component in React, then layer on hand-rolled input validation with useState.

Related examples

Beginner

React Login Form Validation

Build a login form with validation using React Hook Form and Zod.

View example
Intermediate

Form Validation — using Formik

Validate a sign-in form with Formik and Yup — declarative fields, a validation schema, and built-in touched/error state.

View example
Beginner

Login Form UI

A clean, accessible login form component for React with email, password, and social sign-in.

View example