Native Form Validation
title: "Native Form Validation" slug: "web-forms-native-validation" description: "Use HTML5 constraint validation for forms: required, pattern, input types, Constraint Validation API, custom messages, and when to add JavaScript validation." datePublished: "2026-05-04" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "form validation, HTML5 validation, Constraint Validation API, required, pattern, novalidate, checkValidity" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "web-forms-native-validation" slug: "web-forms-native-validation" description: "" datePublished: "2026-07-17" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "web-forms-native-validation" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "web-forms-native-validation" slug: "web-forms-native-validation" description: "" datePublished: "2026-07-17" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "web-forms-native-validation" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "web-forms-native-validation" slug: "web-forms-native-validation" description: "" datePublished: "2026-07-17" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "web-forms-native-validation" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "web-forms-native-validation" slug: "web-forms-native-validation" description: "" datePublished: "2026-07-17" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "web-forms-native-validation" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "web-forms-native-validation" slug: "web-forms-native-validation" description: "" datePublished: "2026-07-17" dateModified: "2026-07-17" tags:
- "Engineering" keywords: "web-forms-native-validation" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
title: "Native Form Validation" slug: "web-forms-native-validation" description: "Use HTML5 constraint validation for forms: required, pattern, input types, Constraint Validation API, custom messages, and when to add JavaScript validation." datePublished: "2026-05-04" dateModified: "2026-07-17" tags:
- "Web"
- "HTML"
- "Forms"
- "Frontend" keywords: "form validation, HTML5 validation, Constraint Validation API, required, pattern, novalidate, checkValidity" faq:
- q: "What is the main production risk with web forms native validation?" a: "Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors."
- q: "When should we prioritize web forms native validation?" a: "Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly."
- q: "How do we validate web forms native validation changes?" a: "Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR."
Our signup form imported a 45KB validation library to check that email fields contained an @ symbol. The browser already did that with type="email". We removed the library, switched to native constraints with custom messages via the Constraint Validation API, and the form worked with JavaScript disabled while giving us styled error states when JS was available.
HTML constraint attributes
<form>
<label for="email">Email</label>
<input
id="email"
name="email"
type="email"
required
autocomplete="email"
/>
<label for="age">Age</label>
<input
id="age"
name="age"
type="number"
min="13"
max="120"
required
/>
<label for="website">Website</label>
<input
id="website"
name="website"
type="url"
placeholder="https://example.com"
/>
<label for="username">Username</label>
<input
id="username"
name="username"
type="text"
pattern="[a-zA-Z0-9_]{3,20}"
title="3-20 characters: letters, numbers, underscores"
required
/>
<button type="submit">Sign up</button>
</form>
Each attribute maps to a validation constraint the browser checks on submit and on blur.
Input types with built-in validation
| Type | Validates |
|---|---|
email |
Contains @ with domain |
url |
Valid URL scheme |
number |
Numeric, respects min/max/step |
tel |
No format validation (intentionally) |
date |
Valid date, respects min/max |
file |
Accept attribute filters MIME types |
Constraint Validation API
Every form control implements the ValidityState interface:
const input = document.getElementById('email');
input.addEventListener('input', () => {
const valid = input.validity.valid;
input.classList.toggle('invalid', !valid);
input.classList.toggle('valid', valid);
});
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity(); // shows native tooltips
}
});
Key properties on input.validity:
validity.valueMissing // required field is empty
validity.typeMismatch // wrong type (email without @)
validity.patternMismatch // doesn't match pattern attribute
validity.tooShort // below minlength
validity.tooLong // above maxlength
validity.rangeUnderflow // number below min
validity.rangeOverflow // number above max
validity.customError // setCustomValidity() was called
Custom error messages
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach((input) => {
input.addEventListener('invalid', (e) => {
e.preventDefault(); // suppress native tooltip
const messages = {
valueMissing: `${input.labels[0]?.textContent} is required`,
typeMismatch: 'Enter a valid email address',
patternMismatch: input.title || 'Invalid format',
rangeUnderflow: `Minimum value is ${input.min}`,
rangeOverflow: `Maximum value is ${input.max}`,
};
for (const [key, message] of Object.entries(messages)) {
if (input.validity[key]) {
showError(input, message);
return;
}
}
});
input.addEventListener('input', () => {
clearError(input);
});
});
Call setCustomValidity('') on input to clear custom errors and allow re-validation.
Styling valid and invalid states
input:user-valid {
border-color: #16a34a;
}
input:user-invalid {
border-color: #dc2626;
}
input:user-invalid:focus {
outline-color: #dc2626;
}
The :user-valid and :user-invalid pseudo-classes apply only after the user interacts with the field, avoiding red borders on untouched required fields at page load.
Cross-field validation
Native validation can't compare two fields. Use JavaScript for password confirmation:
const password = document.getElementById('password');
const confirm = document.getElementById('confirm-password');
function validateMatch() {
if (confirm.value && confirm.value !== password.value) {
confirm.setCustomValidity('Passwords do not match');
} else {
confirm.setCustomValidity('');
}
}
password.addEventListener('input', validateMatch);
confirm.addEventListener('input', validateMatch);
novalidate and server-side validation
Add novalidate to the form when building a fully custom validation UI:
<form novalidate>
This disables native tooltips but keeps the Constraint Validation API functional — you still call checkValidity() programmatically.
Server-side validation is non-negotiable. Client validation improves UX; it does not protect against crafted requests.
Server-side mirror
Mirror client constraints on the server — never trust the browser:
def validate_signup(data):
errors = {}
if not data.get('email') or '@' not in data['email']:
errors['email'] = 'Valid email required'
if not re.match(r'^[a-zA-Z0-9_]{3,20}$', data.get('username', '')):
errors['username'] = '3-20 alphanumeric characters'
return errors
Return field-level errors in a structured format the client can display next to each input.
Live regions for screen readers
When custom validation UI replaces native tooltips, announce errors with ARIA live regions:
<div role="alert" aria-live="polite" id="email-error"></div>
Integration with design systems
Design system inputs should expose validity state to CSS:
.ds-input:user-invalid { border-color: var(--error); }
.ds-input:user-valid { border-color: var(--success-subtle); }
Pair with aria-invalid toggled in invalid event listener—native and ARIA stay synchronized.
Server-side mirror
Duplicate rules server-side—never trust client validation alone. Share constraint definitions via OpenAPI or shared Zod schema code-generated to HTML attributes where possible.
Progressive enhancement without JS
Without JS, native validation still works on submit—ensure form works with full page POST for critical flows (login, payment) even in enhanced SPA mode.
Constraint Validation API events
Listen invalid on form, set custom message with setCustomValidity, call reportValidity() on submit. :user-invalid pseudo-class styles errors only after interaction — better UX than red on first keystroke.
Share rules with Zod
Generate HTML attributes from Zod schema — single source for client native and server validation. minLength, pattern, type=email mirror Zod constraints.
Practical follow-through (1)
Ship the smallest vertical slice first — one route, one widget, one index configuration — with rollback documented before expanding scope. Baseline the user-visible metric this work protects (latency, recall, conversion, task success rate) for seven days before change and seven days after in your largest market.
Compare canary p75 to control before full rollout. Exercise edge paths manually: refresh, back navigation, double-submit, offline mode, and keyboard-only flows. When assumptions change — traffic doubles, vendor upgrades, org restructure — revisit whether the original design still fits; quiet periods hide drift until the next incident.
Practical follow-through (2)
Ship the smallest vertical slice first — one route, one widget, one index configuration — with rollback documented before expanding scope. Baseline the user-visible metric this work protects (latency, recall, conversion, task success rate) for seven days before change and seven days after in your largest market.
Compare canary p75 to control before full rollout. Exercise edge paths manually: refresh, back navigation, double-submit, offline mode, and keyboard-only flows. When assumptions change — traffic doubles, vendor upgrades, org restructure — revisit whether the original design still fits; quiet periods hide drift until the next incident.
Practical follow-through (3)
Ship the smallest vertical slice first — one route, one widget, one index configuration — with rollback documented before expanding scope. Baseline the user-visible metric this work protects (latency, recall, conversion, task success rate) for seven days before change and seven days after in your largest market.
Compare canary p75 to control before full rollout. Exercise edge paths manually: refresh, back navigation, double-submit, offline mode, and keyboard-only flows. When assumptions change — traffic doubles, vendor upgrades, org restructure — revisit whether the original design still fits; quiet periods hide drift until the next incident.
Practical follow-through (4)
Ship the smallest vertical slice first — one route, one widget, one index configuration — with rollback documented before expanding scope. Baseline the user-visible metric this work protects (latency, recall, conversion, task success rate) for seven days before change and seven days after in your largest market.
Compare canary p75 to control before full rollout. Exercise edge paths manually: refresh, back navigation, double-submit, offline mode, and keyboard-only flows. When assumptions change — traffic doubles, vendor upgrades, org restructure — revisit whether the original design still fits; quiet periods hide drift until the next incident.
Practical follow-through (5)
Ship the smallest vertical slice first — one route, one widget, one index configuration — with rollback documented before expanding scope. Baseline the user-visible metric this work protects (latency, recall, conversion, task success rate) for seven days before change and seven days after in your largest market.
Compare canary p75 to control before full rollout. Exercise edge paths manually: refresh, back navigation, double-submit, offline mode, and keyboard-only flows. When assumptions change — traffic doubles, vendor upgrades, org restructure — revisit whether the original design still fits; quiet periods hide drift until the next incident.
Resources
- MDN: Form validation
- Constraint Validation API
- HTML input types
- WCAG: Error identification
- Can I use :user-invalid
Frequently asked questions
What is the main production risk with web forms native validation?
Teams ship without field measurement—web forms native validation failures appear as silent UX regressions, cost drift, or audit findings rather than clear errors.
When should we prioritize web forms native validation?
Prioritize when user research, CrUX, support tickets, or compliance requirements show pain on critical paths—not when a checklist mentions it abstractly.
How do we validate web forms native validation changes?
Baseline RUM before changes, compare p75 after deploy, and keep rollback via feature flags or cache purge documented in the PR.
Hiring a senior Android / Flutter engineer?
I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.
Get in touch →