Advanced20 min read

Form Validation

Master built-in HTML form validation with required fields, patterns, constraints, and learn how to provide custom validation messages without JavaScript frameworks.

Built-in Validation Attributes

HTML provides powerful validation without any JavaScript. When a form is submitted, the browser checks each field against its validation attributes and blocks submission if any field is invalid.

Core validation attributes

AttributeApplies toDescription
requiredAll inputsField must not be empty
minlength / maxlengthText inputsMin/max character count
min / maxNumber, date, rangeMin/max numeric or date value
stepNumber, rangeValid increment (e.g., step="0.01" for cents)
patternText inputsRegular expression the value must match
typeAll inputsBuilt-in validation for email, url, number, date, etc.

The browser shows native error tooltips when validation fails. These messages are localised to the user's language.

The pattern Attribute

The pattern attribute takes a regular expression (without delimiters) and validates the input value against it. The pattern must match the entire value (it is implicitly anchored with ^ and $).

Common patterns

html
<!-- Phone: digits, spaces, dashes, and optional + prefix -->
<input type="tel" pattern="\+?[\d\s-]{7,15}" title="Phone number">

<!-- Postal code (US): 5 digits or 5+4 format -->
<input type="text" pattern="\d{5}(-\d{4})?" title="US ZIP code">

<!-- Username: 3-20 alphanumeric characters -->
<input type="text" pattern="[a-zA-Z0-9]{3,20}" title="3-20 alphanumeric characters">

<!-- No spaces allowed -->
<input type="text" pattern="\S+" title="No spaces allowed">

Always pair pattern with the title attribute — its text is included in the browser's error tooltip to help the user understand the expected format.

CSS Pseudo-classes for Validation

CSS provides pseudo-classes that let you style form elements based on their validation state:

  • :valid — The field passes all validation constraints.
  • :invalid — The field fails at least one constraint.
  • :required — The field has a required attribute.
  • :optional — The field does not have required.
  • :in-range / :out-of-range — For number/date inputs with min/max.
  • :placeholder-shown — The placeholder is currently visible (field is empty).
css
input:valid { border-color: green; }
input:invalid { border-color: red; }
input:required { border-left: 3px solid orange; }

Note: Empty optional fields match :valid, and empty required fields match :invalid as soon as the page loads. To avoid styling empty fields on load, combine with :not(:placeholder-shown) or use the :user-invalid pseudo-class (newer browsers).

Custom Validation Messages

You can override the browser's default error messages using JavaScript's Constraint Validation API:

js
const input = document.getElementById('username');

input.addEventListener('input', () => {
  if (input.validity.tooShort) {
    input.setCustomValidity('Username must be at least 3 characters.');
  } else if (input.validity.patternMismatch) {
    input.setCustomValidity('Only letters and numbers are allowed.');
  } else {
    input.setCustomValidity('');  // Reset — field is valid
  }
});

Key API methods

  • input.setCustomValidity(message) — Sets a custom error message. Pass '' to clear.
  • input.reportValidity() — Triggers validation UI immediately.
  • input.checkValidity() — Returns true/false without showing UI.
  • input.validity — An object with boolean properties: valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow, stepMismatch, customError.

Disabling validation

  • novalidate on <form> — Disables all validation for that form.
  • formnovalidate on <button> — Disables validation for that specific submit button (useful for "Save draft" buttons).

Validated Form Example

html
<form>
  <!-- Required email -->
  <label for="email">Email (required):</label>
  <input type="email" id="email" name="email" required
         placeholder="you@example.com">

  <!-- Username with pattern -->
  <label for="username">Username (3-20 alphanumeric):</label>
  <input type="text" id="username" name="username"
         pattern="[a-zA-Z0-9]{3,20}"
         title="3 to 20 letters or numbers"
         minlength="3" maxlength="20" required>

  <!-- Age with min/max -->
  <label for="age">Age (18-120):</label>
  <input type="number" id="age" name="age"
         min="18" max="120" required>

  <!-- Submit and Save Draft -->
  <button type="submit">Register</button>
  <button type="submit" formnovalidate>Save Draft</button>
</form>

What happens when you add the `novalidate` attribute to a <form> element?

Ready to practice?

Create your free account to access the interactive code editor, run challenges, and track your progress.