Forms That Validate Themselves
Use modern input types and constraint attributes to catch mistakes before JavaScript runs.
The browser ships with a genuinely good validation engine. Most of the client-side checks people write by hand are already built in — you just have to declare your intent with attributes.
Pick the right input type
Beyond text, you have email, url, tel, number, range, date, time, color, and search. Each brings its own on-screen keyboard on mobile and its own built-in checks.
Constraint attributes
required— the field can't be empty.minlength/maxlength— text length bounds.min/max/step— numeric and date bounds.pattern— a regular expression the value must match.
An inline taste of a self-checking field:
<input type="password"
minlength="8"
pattern="(?=.*\d).{8,}"
title="8+ characters, at least one number"
required>The title on a patterned input is not decoration — the browser shows it in the validation bubble when the pattern fails, so write it like a helpful hint.
Example
When to use it
- A registration form uses pattern and minlength attributes so browsers reject weak passwords client-side before any server call.
- A booking system uses novalidate on the form and the Constraint Validation API so a custom styled error UI controls validation.
- An onboarding wizard uses required on all inputs so users cannot advance steps without filling mandatory fields.
More examples
Built-in validation attributes
pattern, minlength, min, max, and required trigger native browser validation without JavaScript.
<form>
<label for="username">Username (3-20 chars, letters only)</label>
<input type="text" id="username" name="username"
minlength="3" maxlength="20"
pattern="[A-Za-z]+" required
title="Letters only, 3-20 characters">
<label for="age">Age (18-99)</label>
<input type="number" id="age" name="age"
min="18" max="99" required>
<button type="submit">Submit</button>
</form>Custom validation with Constraint API
novalidate disables native popups; validity API properties let you show custom accessible error messages.
<form id="signup" novalidate>
<input type="email" id="email" name="email" required>
<span id="email-error" role="alert"></span>
<button type="submit">Register</button>
</form>
<script>
const form = document.getElementById("signup");
form.addEventListener("submit", (e) => {
const email = document.getElementById("email");
if (!email.validity.valid) {
e.preventDefault();
document.getElementById("email-error")
.textContent = "Please enter a valid email.";
}
});
</script>Password confirmation with constraint
setCustomValidity() on the second input makes the browser block submission with a custom mismatch message.
<form id="pw-form">
<input type="password" id="pw1" placeholder="Password" minlength="8" required>
<input type="password" id="pw2" placeholder="Confirm password" required>
<button type="submit">Set password</button>
</form>
<script>
document.getElementById("pw2").addEventListener("input", function() {
const pw1 = document.getElementById("pw1").value;
this.setCustomValidity(this.value === pw1 ? "" : "Passwords do not match.");
});
</script>
Discussion