Forms & validation
Read form data, built-in HTML validation, the Constraint Validation API and custom checks.
Forms are how users talk to your app: sign-ups, searches, checkouts, settings. In this lesson you'll read form data with JavaScript, use the browser's built-in validation, add custom rules with the Constraint Validation API, and show accessible error messages.
A form to work with#
Good forms start with good HTML: every input has a <label>, a name (used when reading data) and the right type (email, number, tel, url, date…) so mobile users get the right keyboard. autocomplete helps password managers and autofill.
Handling submit#
Listen for submit on the form (not click on the button) — it also fires when the user presses Enter:
Reading values
FormDatareads every field with aname.Object.fromEntries(formData)turns it into a plain object.- Values are strings: convert with
Number(values.age). An empty number field gives"". - Unchecked checkboxes are missing from
FormData. For multi-selects or several checkboxes with the same name, usedata.getAll("topics"). - You can also read single fields:
form.elements.email.value,checkbox.checked,select.value.
Built-in HTML validation#
The browser can validate many rules for you, with no JavaScript:
Without novalidate, the browser blocks submission and shows its own bubble messages. That's a fine start, but the bubbles can't be styled and behave differently in each browser. Many apps add novalidate and use the same rules from JavaScript to show custom messages — which is what we'll do.
CSS hooks
:user-invalid only applies after the user has interacted with the field, so the form doesn't look angry before they've typed anything.
The Constraint Validation API#
Every input has validation built into its JavaScript object:
validity has flags such as valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow and customError.
Custom rules with setCustomValidity
A non-empty message makes the field invalid; you must reset it to "" when the problem is fixed.
Custom, accessible error messages#
Let's validate the whole form ourselves and show messages next to each field:
Accessibility details that matter:
aria-invalid="true"tells screen readers the field has a problem.aria-describedbylinks the field to its error text, so it's read out.- Moving focus to the first invalid field helps keyboard users.
- Validate on blur/focusout or submit — not on every keystroke, which is distracting.
Pure validation functions#
Keep your rules in plain functions — they're easy to test in Node and can be shared with the server:
Submitting with fetch#
After validation, send the data to a server (you'll learn fetch properly in fetch, APIs & JSON):
Other useful form events#
Common mistakes#
- Listening for
clickon the submit button instead ofsubmiton the form. - Forgetting
nameattributes —FormDataignores unnamed fields. - Treating number inputs' values as numbers — they're strings.
- Forgetting
setCustomValidity(""), leaving a field permanently invalid. - Trusting client-side validation for security. Always validate on the server too.
- Placeholder text instead of labels — placeholders disappear as you type and are often low-contrast.
What's next#
Next you'll make data survive a page refresh with localStorage and sessionStorage.
Check your understanding
Quick quiz
1.Why call
event.preventDefault()in a form'ssubmithandler?2.What does
new FormData(form).get("email")return?3.Where must validation *always* happen for security?
Finished reading?
Mark this lesson complete to track your progress.