🎉
Lesson Complete!
Forms are validated! Last forms lesson: select dropdowns and textarea.
📝 Phase 5 · Forms🟡 IntermediateModule 06

Form Validation

⏱ 20 min read📖 4 sections🧩 5-question quiz
Course Progress57% complete
HTML5 ships a complete validation engine that runs before the form ever reaches your server. With the right attributes you can enforce required fields, minimum lengths, number ranges, and custom regex patterns — all without writing a single line of JavaScript. You also get CSS hooks to style valid and invalid states in real time.

Validation Attributes

These attributes define the rules — the browser enforces them automatically on submit.

AttributeWorks withWhat it validates
requiredAll inputs, select, textareaField must not be empty before submitting
minlengthtext, email, password, search, url, telMinimum number of characters
maxlengthtext, email, password, search, url, telMaximum number of characters (hard limit — prevents typing more)
minnumber, range, date, timeMinimum allowed value
maxnumber, range, date, timeMaximum allowed value
patterntext, email, url, tel, searchValue must match this regular expression
typeAllBuilt-in format validation (email must have @, url must have protocol, etc.)
HTMLValidation attributes in action
<!-- Required + length limits -->
<input type="text" name="username"
       required minlength="3" maxlength="20">

<!-- Email type validates format automatically -->
<input type="email" name="email" required>

<!-- Number with min/max range -->
<input type="number" name="age"
       min="18" max="120" required>

<!-- Pattern: UK postcode -->
<input type="text" name="postcode"
       pattern="[A-Z]{1,2}[0-9][0-9A-Z]?\s?[0-9][A-Z]{2}"
       title="Enter a valid UK postcode e.g. SW1A 1AA"
       required>
💡
Always pair pattern with title
When a pattern fails, browsers show a generic "Please match the requested format" message. The title attribute text is shown alongside it to explain what format is expected. Without it, users are left guessing.

Live Demo — Try It

Type into the fields below and watch the border change colour as you go — green when valid, red when invalid.

Border turns green when 3+ characters entered
Must contain @ and a valid domain
Enter a number outside 18–120 to see the red state
CSSStyling valid and invalid states
/* Only style after the user has interacted — */
/* :placeholder-shown = input is still empty   */
input:valid:not(:placeholder-shown) {
  border-color: #2de8c0;  /* green border */
  background: rgba(45,232,192,.05);
}

input:invalid:not(:placeholder-shown) {
  border-color: #f87171;  /* red border */
  background: rgba(248,113,113,.05);
}
:valid — field passes all rules
:invalid — field fails a rule

Custom Error Messages

The browser's built-in error bubbles are functional but plain. You can intercept the validation event with JavaScript and show custom messages using the Constraint Validation API:

HTML + JSCustom validation messages
<form id="myForm" novalidate>
  <label for="em">Email</label>
  <input type="email" id="em" name="email" required>
  <span id="emErr" class="error"></span>
  <button type="submit">Submit</button>
</form>

<script>
const form = document.getElementById('myForm');
form.addEventListener('submit', e => {
  const input = document.getElementById('em');
  const err   = document.getElementById('emErr');
  if (!input.validity.valid) {
    e.preventDefault();
    if (input.validity.valueMissing)   err.textContent = 'Email is required.';
    else if (input.validity.typeMismatch) err.textContent = 'Please enter a valid email.';
  } else { err.textContent = ''; }
});
</script>
🧠
validity state flags you can check
valueMissing (required empty) · typeMismatch (wrong format) · patternMismatch · tooShort / tooLong · rangeUnderflow / rangeOverflow · valid (all rules pass)

novalidate & Server Validation

Adding novalidate to the <form> element disables all browser validation — useful when you're writing your own JavaScript validation or using a form library. The inputs still carry their constraints for the Constraint Validation API to read; the browser just won't block submission.

🔒
Browser validation is a UX layer, not security
Anyone can disable browser validation with DevTools or by sending a crafted HTTP request. Always validate input on the server too. HTML validation improves user experience and reduces bad submissions — it does not prevent malicious data from reaching your server.
HTMLnovalidate for custom JS validation
<!-- Browser skips its own validation -->
<!-- but all HTML5 constraint attributes still work via JS API -->
<form action="/submit" method="post" novalidate>
  <input type="email" name="email" required>
  <!-- Your JS checks input.validity.* and shows your own UI -->
  <button type="submit">Submit</button>
</form>
🧩 Quick Check — Lesson 20
5 questions · instant feedback
1. Which attribute makes a text input reject values shorter than 8 characters?
2. Which CSS pseudo-class matches an input that currently fails its validation rules?
3. Why is it common to combine :invalid with :not(:placeholder-shown)?
4. Is HTML5 form validation sufficient to protect your server from bad data?
5. What does the pattern attribute accept?
🏆
Quiz Complete!
Form validation done! Final forms lesson: select dropdowns and textarea.
🏆
Lesson 20 Challenge
Code exercise · 20 min
Build a validated registration form with custom CSS feedback states.

Create a sign-up form with these validated fields:
- Username: required, minlength 4, maxlength 16, pattern [a-zA-Z0-9_]+ (alphanumeric + underscore only)
- Email: type="email", required
- Password: type="password", required, minlength 8
- Age: type="number", min 13, max 120, required

CSS requirements:
- Inputs show a green border + subtle background when :valid (and not empty)
- Inputs show a red border when :invalid (and not empty)
- A ✓ or ✗ icon appears after each input using ::after on a wrapper
- The submit button is semi-transparent until the form is :valid (use form:valid + button styling)
Show hints
  • Use :invalid:not(:placeholder-shown) to avoid red styling on pristine empty inputs
  • For form:valid button: form:valid button[type=submit] { opacity: 1; background: ...; }
  • Icons via CSS: wrap input in a div, add position: relative and use ::after with content: '✓' or '✗'
  • Pattern [a-zA-Z0-9_]+ means: one or more chars from that set — the browser anchors it automatically