Formclick

HTML Form Validation Explained

How HTML form validation works — built-in browser checks, the Constraint Validation API, styling valid and invalid fields, and why your server must validate too.

FFormclick Team4 min read
Close-up of a red check mark on a crisp white paper with black boxes, symbolizing completion.
Photo by Tara Winstead on Pexels
On this page
  1. The three layers of validation
  2. Layer 1: Built-in HTML validation
  3. Layer 2: The Constraint Validation API
  4. Styling valid and invalid fields
  5. When validation should happen
  6. Making validation accessible
  7. Layer 3: Server-side validation
  8. Quick checklist

Validation is how a form makes sure the data it receives is complete and sensible: the email looks like an email, the required fields aren't empty, the date isn't in the past. Good validation helps people fix mistakes before they hit Submit. Bad validation nags, confuses and costs you submissions.

This guide explains every layer of HTML form validation and how they fit together.

The three layers of validation

  1. Built-in HTML validation — attributes like required and type="email". Free, fast and accessible.
  2. JavaScript validation — custom rules and custom messages, using the browser's Constraint Validation API.
  3. Server-side validation — the only layer you can trust, because anyone can bypass the first two.

You want all three. The first two are for user experience; the third is for correctness and security.

Layer 1: Built-in HTML validation

Add attributes and the browser checks the field when the form is submitted, shows a message, and refuses to send until it's fixed.

<form action="https://api.formclick.io/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">

  <label for="name">Name</label>
  <input id="name" name="name" required minlength="2" maxlength="80">

  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>

  <label for="guests">Guests</label>
  <input id="guests" name="guests" type="number" min="1" max="10" required>

  <label for="code">Promo code (optional)</label>
  <input id="code" name="promo" pattern="[A-Z0-9]{6}" title="6 capital letters or digits">

  <button type="submit">Book</button>
</form>
Attribute What it checks
required The field isn't empty (or a checkbox is ticked, or a radio in the group is chosen)
type="email", type="url" The value has the right format
minlength, maxlength Text length
min, max, step Numbers, dates and times within range and on a valid step
pattern The value matches a regular expression (the whole value, not part)
accept Hints which file types the picker shows (not enforced)

Full details in HTML Form Attributes Explained.

Layer 2: The Constraint Validation API

Every form control has a validity object describing exactly what's wrong:

const email = document.getElementById('email');

email.validity.valueMissing;  // true if required and empty
email.validity.typeMismatch;  // true if not a valid email
email.validity.tooShort;      // shorter than minlength
email.validity.patternMismatch;
email.validity.rangeUnderflow; // below min
email.validity.valid;         // true if everything passes

And a few methods to work with it:

  • checkValidity() — returns true/false without showing anything.
  • reportValidity() — checks and shows the browser's message.
  • setCustomValidity(message) — marks the field invalid with your own message (an empty string clears it).

That last one lets you add rules HTML can't express, like "passwords must match":

const password = document.getElementById('password');
const confirm = document.getElementById('confirm');

confirm.addEventListener('input', function () {
  confirm.setCustomValidity(
    confirm.value === password.value ? '' : 'Passwords do not match.'
  );
});

The browser now treats a mismatch like any other validation error — the form won't submit and the message appears. Read more in How to Display Custom Form Validation Messages.

Styling valid and invalid fields

CSS has pseudo-classes for validation state:

  • :valid / :invalid — match immediately, even before the user types. Using :invalid paints an empty required form red on page load.
  • :user-valid / :user-invalid — match only after the user has interacted with the field. These are what you usually want.
input:user-invalid {
  border-color: #dc2626;
}
input:user-invalid + .error {
  display: block;
}
input:user-valid {
  border-color: #16a34a;
}

When validation should happen

Moment Good for
On submit Always — the final check
On blur (leaving a field) Most fields — people have finished typing
While typing Only for helpful, positive feedback (password strength, characters left)

Showing "Invalid email" after the first keystroke is the most common validation anti-pattern. Wait until the person leaves the field or submits.

Making validation accessible

  • Keep messages next to the field and link them with aria-describedby.
  • Set aria-invalid="true" on fields with errors when you manage errors yourself.
  • Don't rely on colour — always include text.
  • Move focus to the first invalid field on submit so keyboard and screen-reader users know where to go.
  • Summarise errors at the top of long forms, with links to each field.

Layer 3: Server-side validation

Browser validation can be skipped by disabling JavaScript, editing the page, or sending a request directly with a script. Your server — or form backend — must validate everything again: required fields, formats, lengths, allowed values and file types.

If you use Formclick, submissions are checked on the server, filtered for spam and limited in size before they reach your inbox or integrations. You still decide what fields mean — for example, a booking system should reject dates in the past even if the form already did.

More on this split in Client-Side vs Server-Side Form Validation.

Quick checklist

  • Use the right type for every field.
  • Add required only where you truly need the data.
  • Write helpful title text for every pattern.
  • Style with :user-invalid, not :invalid.
  • Validate on blur and submit, not on every keystroke.
  • Always validate again on the server.

Next: How to Validate Email Addresses in HTML Forms and How to Validate Phone Numbers in HTML Forms.

Found this useful? Share it.

All articles