Form Validation

Browser-Native Validation Catches Most Mistakes

Form Validation

HTML has built-in validation attributes — `required`, `type`, `pattern`, `min`, `max`. The browser handles error messages and blocks submit until they pass.

3 min read Level 1/5 #html#forms#validation
What you'll learn
  • Apply built-in validation attributes
  • Show validation hints to users
  • Recognize when JS validation adds value

HTML has built-in form validation. The browser checks values on submit and refuses to send the form if anything fails.

Validation Attributes

AttributeEffect
requiredMust be filled in
typeemail, url, number, etc. enforce shape
min / maxRange for numbers and dates
minlength / maxlengthCharacter count limits
patternA regex the value must match
stepFor numbers: enforce increments
<form>
  <label>Email
    <input type="email" name="email" required />
  </label>

  <label>Username
    <input
      type="text"
      name="username"
      required
      minlength="3"
      maxlength="20"
      pattern="[a-zA-Z0-9_]+"
      title="Letters, numbers, and underscores only"
    />
  </label>

  <label>Age
    <input type="number" name="age" min="13" max="120" />
  </label>

  <button>Sign up</button>
</form>

Submitting with any invalid value shows the native error and stops submission.

Styling Validation States

CSS pseudo-classes:

input:invalid {
  border-color: red;
}
input:valid {
  border-color: green;
}

/* `:user-invalid` only applies AFTER the user has interacted */
input:user-invalid {
  border-color: red;
}

:user-invalid is the better default — :invalid fires before the user has typed anything, which is rarely the right UX.

Custom Messages

You can replace the default browser message with JS:

input.addEventListener("invalid", () => {
  if (input.validity.tooShort) {
    input.setCustomValidity("Username must be at least 3 characters.");
  } else {
    input.setCustomValidity("");
  }
});

When JS Validation Adds Value

Built-in validation is great for shape and presence. Reach for JS for:

  • Async checks (does this username exist?)
  • Cross-field rules (password and confirm-password must match)
  • Showing inline errors instead of native popups
  • Submitting valid data via fetch without page reload

The HTML validation still runs first — JS extends it.

Skipping Validation

novalidate on the form:

<form action="/save" method="POST" novalidate>...</form>

Useful when you handle validation entirely in JS.

Up Next

What happens when the form actually submits.

Form Submit →