Form Validation
Browsers can check form input before it's submitted, with no JavaScript. Add constraints as attributes:
<form action="/register" method="post">
<label for="user">Username (3–16 letters or digits)</label>
<input id="user" name="user" required minlength="3" maxlength="16" pattern="[A-Za-z0-9]+">
<label for="mail">Email</label>
<input id="mail" name="mail" type="email" required>
<label for="age">Age</label>
<input id="age" name="age" type="number" min="13" max="120" step="1">
<label for="start">Start date</label>
<input id="start" name="start" type="date" min="2026-01-01">
<label><input type="checkbox" name="terms" required> I accept the terms</label>
<button>Create account</button>
</form>
| Attribute | Constraint |
|---|---|
required |
Must not be empty (checkbox: must be ticked; radio group: one chosen) |
minlength / maxlength |
Text length limits (maxlength also stops typing) |
min / max / step |
Range and granularity for numbers, dates, times, and ranges |
type="email" / type="url" |
Value must look like an email / absolute URL |
pattern |
Value must match a regular expression — the whole value |
When someone submits with a problem, the browser cancels the submission, focuses the first invalid field, and shows a message in the visitor's language.
pattern
pattern is a JavaScript regular expression that must match the
entire value (it's implicitly anchored with ^(?: ... )$), and it only
applies when the field isn't empty. Explain the format in the label or a
hint, because the browser's default message just says the format is
wrong. Test patterns with our Regex Tester.
Checking validity from JavaScript
Each control has a validity object you can inspect. This page fills in
some bad values and prints what the browser thinks:
<form>
<input id="user" required minlength="3" pattern="[A-Za-z0-9]+" aria-label="Username">
<input id="age" type="number" min="13" max="120" aria-label="Age">
<input id="mail" type="email" aria-label="Email">
</form>
<script>
const user = document.getElementById("user");
const age = document.getElementById("age");
const mail = document.getElementById("mail");
console.log("empty user valid?", user.checkValidity(), user.validity.valueMissing);
user.value = "ada lovelace";
console.log("pattern mismatch?", user.validity.patternMismatch);
age.value = "7";
console.log("too small?", age.validity.rangeUnderflow);
mail.value = "ada@example";
console.log("email valid?", mail.validity.valid);
mail.value = "not-an-email";
console.log("email valid now?", mail.validity.valid, mail.validity.typeMismatch);
</script>
empty user valid? false true
pattern mismatch? true
too small? true
email valid? true
email valid now? false true
Note that ada@example counts as a valid email — the check is loose on
purpose (addresses on internal networks don't need a dot). Only sending
a confirmation email proves an address works.
Styling valid and invalid fields
CSS can style fields by state: :invalid, :valid, :required, and
:user-invalid (invalid after the visitor has interacted with the
field, so an empty form isn't red on page load). See
CSS for selectors.
Turning validation off
novalidate on the form (or formnovalidate on one button, like "Save
draft") skips built-in validation — useful when JavaScript shows its own
messages.
Client-side validation is not security
Anyone can edit your HTML in developer tools or send requests without a browser. Always validate again on the server. Browser validation is for a better experience, not for protection.
Practice
Write an input for a UK-style product code: exactly three capital letters,
a hyphen, and four digits (like ABC-1234), required, with a hint in the
label.
Show solution
<label for="code">Product code (for example ABC-1234)</label>
<input id="code" name="code" required pattern="[A-Z]{3}-[0-9]{4}">