We’ll look at the HTML constraint validation API. Built-in form rules, custom messages, and JavaScript hooks that work with native browser behaviour instead of fighting it.
Built-in constraints
<form id="signup" novalidate>
<label>
Email
<input name="email" type="email" required autocomplete="email">
</label>
<label>
Age
<input name="age" type="number" min="13" max="120" required>
</label>
<button type="submit">Create account</button>
</form>
novalidate on the form lets you handle submission in JS while still using each field’s validation API. Without it, the browser blocks submit and shows native bubbles.
Checking validity in JavaScript
const form = document.querySelector('#signup');
form.addEventListener('submit', (event) => {
event.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
submitForm(new FormData(form));
});
Custom messages
const email = form.elements.email;
email.addEventListener('input', () => {
if (email.validity.valueMissing) {
email.setCustomValidity('We need an email to create your account.');
} else if (email.validity.typeMismatch) {
email.setCustomValidity('That does not look like an email address.');
} else {
email.setCustomValidity('');
}
});
Always clear custom validity on valid input – otherwise the field stays invalid forever.
validity flags
valueMissing– required empty fieldtypeMismatch– wrong type (email, url)patternMismatch– regex pattern failedtooShort/tooLong– length constraintsrangeUnderflow/rangeOverflow– number/date min/max
Showing errors in your UI
Native bubbles are fine for quick prototypes. Product sites usually render inline errors and tie them to inputs with aria-invalid and aria-describedby. Read validity flags, map them to messages, focus the first invalid field.
function showFieldError(input, message) {
input.setAttribute('aria-invalid', 'true');
const hint = document.querySelector(`#${input.id}-error`);
hint.textContent = message;
input.setAttribute('aria-describedby', hint.id);
}
Next: browser storage – localStorage, sessionStorage, and a sensible intro to IndexedDB.

