Built-in Validation
People make mistakes in forms: a field left empty, an email address without its at sign, a year typed as 20025. The browser can catch many of these before the form is sent. It checks the answers against rules written in the HTML, and if one fails it shows a message and stops the submit, with no JavaScript at all. These checks are called validation. They are a convenience for honest visitors, and no protection at all against dishonest ones.
A spell-checker does the same kind of job. It underlines typing slips as you type and saves you from sending them. It cannot stop anyone who wants to send nonsense: they can ignore it, or switch it off. Browser validation works the same way, and this page covers both halves, what it catches and why it can never be the last check.
The Attributes That Set the Rules
Each rule is an attribute on a field. required means the field must not be empty. minlength and maxlength set how many characters text may have. min and max set the lowest and highest allowed number or date. pattern describes the exact shape a text answer must have. And some types bring a check of their own: an email field must look like an email address, a url field like a web address, a number field like a number.
<form id="contact-form"> <label for="name">Your name</label> <input type="text" id="name" name="name" autocomplete="name" required> <label for="email">Email address</label> <input type="email" id="email" name="email" autocomplete="email" required> <label for="message">Message</label> <textarea id="message" name="message" rows="6" required></textarea> <button type="submit">Send message</button> </form>
This is the site's contact form from the previous topic with one word added to each field: required. Press the button with everything empty and the page does not reload. Instead the browser moves the cursor into the name field and shows a small message bubble beside it, asking you to fill it in. Fill in a name, type only "you" as the email, and press the button again: the bubble now points at the email field and says the address is missing an at sign.
The email check is a check of shape: some text, an at sign, more text. It does not know whether the address exists or whether anyone reads it. That is all a browser can know, and it is enough to catch the common slips.
When no attribute fits, pattern describes the shape. Its value is a regular expression, a compact language for describing text, used in almost every programming language. This book uses only the small piece below.
<label for="username">Username</label> <input type="text" id="username" name="username" required pattern="[A-Za-z0-9]{3,16}" title="3 to 16 letters or numbers, no spaces">
The square brackets list the characters allowed: capital letters, small letters and digits. The curly braces say how many: from 3 to 16. The whole answer must match, not just part of it, so "my name" fails because of its space. When it fails, the browser's message says the format does not match, and most browsers add the text of the title attribute, which is why it is written for people. One detail catches everyone once: pattern only checks a field with something in it. An empty field passes unless it is also required.
What Does the Visitor See?
When a check fails, the browser cancels the submit, moves focus to the first field that failed, and shows its message there. It shows one message at a time: fix that field, press the button again, and the next problem appears. Nothing is sent until every rule passes.
The message belongs to the browser, not to your page. Chrome, Firefox and Safari each word it differently and draw it differently, and the language follows the visitor's browser settings, not your page. You cannot restyle the bubble. That is fine for most forms, and it is the reason some sites write their own messages into the page, which the next topic does for one rule.
Styling Valid and Invalid
CSS can style a field by whether it passes. The :invalid pseudo-class, a selector for a state like the focus and hover states of Chapter 3, matches any field that currently fails a rule. It has a problem: an empty required field fails from the moment the page loads. A rule on :invalid paints every empty required field as an error before the visitor has typed a single letter.
:user-invalid waits. It matches a failing field only after the visitor has interacted with it, by changing it and moving on, or by trying to submit the form. It is Baseline widely available since May 2026, so it works in all three current browsers.
input:user-invalid,
textarea:user-invalid {
border: 2px solid crimson;
}
Load the Contact page with this rule and every field looks normal. Click into the email field, type "you" and press Tab: that field alone gets a thick red border. The empty name field stays calm until you press the button, and then every field that fails turns red at once. Colour is only a hint, though. Some visitors cannot tell red from grey, so the words of the message matter more than the border.
DevTools shows which state a field is in. Select the field in the Elements panel and look at the Styles pane: while the field fails and the visitor has touched it, your :user-invalid rule is listed among the rules that apply. Fix the value and the rule drops out of the list.
A Courtesy, Not Security
Every rule on this page runs in the visitor's browser, on the visitor's device, and the visitor controls both. Open the Contact page, open the Elements panel, and find the name input. Double-click the word required in its tag, delete it, and press Enter.
<!-- Before, in the Elements panel --> <input type="text" id="name" name="name" autocomplete="name" required> <!-- After deleting one word --> <input type="text" id="name" name="name" autocomplete="name">
Now leave the name empty, fill in the other two fields and press the button. The form is sent without complaint, and the address bar shows the name pair with nothing after its equals sign. Your rule did not fail; for this visitor it no longer existed. That took ten seconds and no special skill.
It gets simpler still. Any program can send a request straight to a form's action address without loading your page at all, the way APIs for Beginners sends requests with curl, a small program that sends a request from a typed command. That request never passes through your HTML, so none of its rules apply. The server that receives a form must check every value again, every time. How a server does that is in Backend Deep Dive; why people send it bad data on purpose is in Security for Beginners.
So why bother with browser checks? Because most visitors mean well and make ordinary slips, and catching those instantly, before the page leaves, saves them a wait and an error page. The browser helps the visitor; the server decides what to accept.
- "Validation in the browser keeps bad data out." It keeps honest mistakes out. Anyone can switch it off or skip the page, so the server must check again.
- "A pattern is how to validate an email address."
type="email"already checks the shape sensibly. A home-made pattern usually ends up rejecting real addresses. - "Red borders on an empty form help the visitor." Errors shown before anyone has typed feel hostile.
:user-invalidwaits until the visitor has had a go.
- Most form mistakes are caught with a few attributes and no script.
- Its limit is the first real security boundary in this book: anything that must be enforced is enforced on the server.
Knowledge Check
A username must be between 3 and 16 characters long. Which attributes express that rule?
- The attributes
min="3"andmax="16" - The attributes
minlength="3"andmaxlength="16" - The single attribute
required="3-16" - A
patternattribute with the value 3-16
Why does a rule on input:invalid feel hostile on a brand-new form?
- It paints every field as an error, even valid ones
- It hides the browser's own error messages from view
- Empty required fields match it before anyone types
- It only starts to work after the form has been sent
Why must the server check a form's data again, even with required and pattern in place?
- Anyone can delete the rules or skip the page entirely
- Browsers run the checks on some devices but not others
- The browser's checks are too slow to trust on real forms
- The browser forgets its rules after the first submit
What does type="email" check before the form is sent?
- That the address exists and can receive mail
- That the address belongs to the person typing
- That the address has not been used on this site before
- That the value has the shape of an address
You got correct