Build a Form Validator with JavaScript

Build a form validator with plain JavaScript that checks required fields, validates an email format, and shows clear error messages before submission.

8 min read

A JavaScript form validator checks that every field in a form is filled in correctly before the form is allowed to submit. It builds on the form submission pattern from other mini projects, but adds a validation step in between: the click is caught, checked against a set of rules, and only allowed through if every rule passes.

You will build a form with a name, email, and password field, write a small check for each one, and show clear error messages when a field fails.

What You Will Build

The validator needs to support:

  • Blocking submission until every required field has a value.
  • Checking that the email field looks like a real email address.
  • Showing an error message next to each invalid field.
  • Allowing the form to submit once every check passes.
Form validation flow

Every submission runs through the same two-branch decision: if any check fails, the user sees exactly what to fix, and if every check passes, the form is allowed to continue. The steps below build each piece of that decision.

Step 1: Build the HTML

Start with a form that has a spot for an error message under each field.

htmlhtml
<form id="signupForm">
  <input type="text" id="name" placeholder="Name" />
  <span class="error" id="nameError"></span>
 
  <input type="text" id="email" placeholder="Email" />
  <span class="error" id="emailError"></span>
 
  <input type="password" id="password" placeholder="Password" />
  <span class="error" id="passwordError"></span>
 
  <button type="submit">Sign Up</button>
</form>

Each field has its own error element right below it. Placing errors near the field they belong to is clearer for the user than one general error message at the top of the form.

Step 2: Catch the Submission Before It Reloads the Page

Listen for the submit event, and stop the browser's default reload before running any checks.

javascriptjavascript
const form = document.getElementById("signupForm");
 
form.addEventListener("submit", (event) => {
  event.preventDefault();
 
  const errors = validateForm();
  if (errors.length === 0) {
    form.submit();
  }
});

event.preventDefault() stops the browser from reloading the page immediately, which gives the validation code a chance to run first. The handler collects every error into an array, and only calls the real submit when that array comes back empty. For more on this event, see the using preventDefault in JavaScript guide.

Step 3: Check Required Fields

Write a small function that checks a field is not left empty, and shows an error if it is.

javascriptjavascript
function validateRequired(inputId, errorId, message) {
  const input = document.getElementById(inputId);
  const errorEl = document.getElementById(errorId);
 
  if (input.value.trim() === "") {
    errorEl.textContent = message;
    return message;
  }
 
  errorEl.textContent = "";
  return null;
}

This function reads one field's value, and either sets an error message next to it or clears any error from a previous attempt. It returns the message string when the field fails, or null when it passes, so the calling code can tell the two cases apart.

Step 4: Check the Email Format

Email needs an extra check beyond just being non-empty, using a regular expression to catch an obviously invalid format.

javascriptjavascript
function validateEmail(inputId, errorId) {
  const input = document.getElementById(inputId);
  const errorEl = document.getElementById(errorId);
  const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
 
  if (!pattern.test(input.value.trim())) {
    errorEl.textContent = "Enter a valid email address";
    return "Enter a valid email address";
  }
 
  errorEl.textContent = "";
  return null;
}

The pattern checks for text, an @ symbol, more text, a dot, then more text, which catches obvious mistakes like a missing @ or a missing domain. It cannot confirm the address is real, only that it is shaped like one.

Step 5: Check the Password Length

The password field needs its own rule too, since a required check alone would accept a single character.

javascriptjavascript
function validatePassword(inputId, errorId) {
  const input = document.getElementById(inputId);
  const errorEl = document.getElementById(errorId);
 
  if (input.value.length < 8) {
    errorEl.textContent = "Password must be at least 8 characters";
    return "Password must be at least 8 characters";
  }
 
  errorEl.textContent = "";
  return null;
}

This follows the same shape as validateEmail, but checks length instead of matching a pattern. Any field-specific rule can be added this way: read the value, check it, set or clear the error message, and return the message or null.

Step 6: Combine Every Check

The main validation function calls each individual check and collects the results.

javascriptjavascript
function validateForm() {
  const results = [
    validateRequired("name", "nameError", "Name is required"),
    validateEmail("email", "emailError"),
    validatePassword("password", "passwordError"),
  ];
 
  return results.filter((message) => message !== null);
}

Each check runs regardless of whether an earlier one failed, so the user sees every problem at once instead of one at a time. Filtering out the null values leaves only the real error messages, and an empty array means the form is ready to submit. For a deeper look at rules like this, see the form validation with JavaScript guide.

Common Mistakes

MistakeWhy it breaksFix
Forgetting event.preventDefault()The page reloads before any validation code can runCall it as the first line inside the submit handler
Stopping at the first failed fieldThe user has to resubmit repeatedly to find every mistakeRun every check and collect all the error messages together
Treating a passed email pattern as a verified addressA regular expression only checks the shape of the textUse client-side validation for early feedback, and verify the address for real on the server

Next Step

You have now built the full set of DOM, state, and array patterns used across this mini project series. If you have not built the JavaScript shopping cart yet, it is a good next step for practicing the same event delegation and array-driven rendering used here.

Rune AI

Rune AI

Key Insights

  • Call event.preventDefault() first so the page does not reload before validation can run.
  • Write one small function per rule, such as checking a field is not empty or matches an email pattern.
  • Collect every error into an array instead of stopping at the first one, so the user sees all the problems at once.
  • Only allow the form to submit for real once the error array comes back empty.
  • Show error messages near the field they belong to, not only in one general message at the top.
RunePowered by Rune AI

Frequently Asked Questions

Why block form submission with event.preventDefault() before validating?

Without it, the browser submits and reloads the page as soon as the button is clicked, which happens before your validation code gets a chance to check anything or show an error.

Is a regular expression enough to fully validate an email address?

No. A regular expression can catch obviously malformed input, like a missing @ symbol, but the only way to confirm an email address is real is to send a message to it. Client-side validation is about catching mistakes early, not proving the address exists.

Why collect all the error messages before showing any of them?

Showing one error at a time forces the user to resubmit repeatedly to discover each problem. Collecting every error first and showing them all together saves the user from a frustrating back and forth.

Conclusion

A form validator combines an event you already know from other projects, form submission, with checks that run before anything is sent. Blocking submission until every field passes, then showing every problem at once, is the same approach real signup and checkout forms use to catch mistakes before they reach a server.