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.
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 2Build a login form component in React, then layer on hand-rolled input validation with useState.
Related examples
React Login Form Validation
Build a login form with validation using React Hook Form and Zod.
View exampleForm Validation — using Formik
Validate a sign-in form with Formik and Yup — declarative fields, a validation schema, and built-in touched/error state.
View exampleLogin Form UI
A clean, accessible login form component for React with email, password, and social sign-in.
View example