html
/

HTML Form Validation: Built-in Browser Validation (Complete Guide)

Last Sync: Today

On this page

18
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

html

HTML Form Validation: Built-in Browser Validation (Complete Guide)

What is HTML Form Validation?

HTML form validation is a built-in browser feature that checks user input before form submission. It prevents incomplete or incorrectly formatted data from being sent to your server, improving data quality and user experience — all without writing a single line of JavaScript.

How Browser Validation Works

When a user tries to submit a form, the browser automatically checks all form controls against their validation attributes. If any field fails validation:

  • The submission is blocked
  • The first invalid field gets focus
  • A default error bubble appears (customizable)
  • The :invalid CSS pseudo-class is applied

Complete HTML Validation Attributes

AttributeTypeDescriptionExample
`required`BooleanField cannot be empty`<input required>`
`minlength`NumberMinimum character length`<input minlength="3">`
`maxlength`NumberMaximum character length`<input maxlength="20">`
`min`Number/DateMinimum numeric/date value`<input type="number" min="18">`
`max`Number/DateMaximum numeric/date value`<input type="date" max="2026-12-31">`
`step`NumberIncrement/decrement step`<input type="number" step="5">`
`pattern`RegexCustom validation using regex`<input pattern="[A-Za-z]{3}">`
`multiple`BooleanMultiple values (email/file)`<input type="email" multiple>`

Input Type-Based Validation (Automatic)

Input TypeBuilt-in ValidationExample
`email`Requires `@` and domain (e.g., `name@domain.com`)`<input type="email">`
`url`Requires valid protocol + domain (e.g., `https://example.com`)`<input type="url">`
`number`Only numeric characters, respects `min`/`max``<input type="number">`
`tel`No automatic format — use `pattern` for phone numbers`<input type="tel" pattern="[0-9]{10}">`
`date`Valid date format (`YYYY-MM-DD`)`<input type="date">`

Real-World Validation Examples

  1. Signup Form with Multiple Constraints

HTMLRead-only
1
<form action="/signup" method="post">
  <!-- Username: 3-20 letters/numbers/underscore -->
  <label>Username:</label>
  <input type="text" 
         pattern="[A-Za-z0-9_]{3,20}" 
         required
         title="3-20 letters, numbers, or underscore">

  <!-- Email with domain restriction via pattern -->
  <label>Email:</label>
  <input type="email" 
         required
         pattern=".+@(gmail|yahoo|outlook)\.com$"
         title="Only Gmail, Yahoo, or Outlook">

  <!-- Password with length and character requirements -->
  <label>Password:</label>
  <input type="password" 
         minlength="8" 
         maxlength="30"
         required>

  <!-- Age between 18-99 -->
  <label>Age:</label>
  <input type="number" 
         min="18" 
         max="99" 
         required>

  <button type="submit">Sign Up</button>
</form>

  1. Phone Number Validation (International Format)

HTMLRead-only
1
<!-- US/Canada: 10 digits, optional dashes/spaces -->
<input type="tel" 
       pattern="[0-9]{3}[-. ]?[0-9]{3}[-. ]?[0-9]{4}"
       placeholder="123-456-7890"
       required>

<!-- International (basic E.164 format) -->
<input type="tel" 
       pattern="\+[1-9][0-9]{7,14}"
       placeholder="+1234567890">

  1. Credit Card Number (Luhn-ready pattern)

HTMLRead-only
1
<!-- 16 digits, optional spaces/hyphens -->
<input type="text" 
       pattern="[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}"
       inputmode="numeric"
       placeholder="1234 5678 9012 3456">

Styling Validation with CSS

Use CSS pseudo-classes to give visual feedback without JavaScript.

CSSRead-only
1
/* Valid fields */
input:valid {
  border: 2px solid green;
  background: #e8f5e9;
}

/* Invalid fields */
input:invalid {
  border: 2px solid red;
  background: #ffebee;
}

/* Required fields */
input:required {
  border-left: 4px solid orange;
}

/* Optional fields */
input:optional {
  opacity: 0.9;
}

/* When field is focused and invalid */
input:focus:invalid {
  outline: none;
  box-shadow: 0 0 5px red;
}

/* Custom error message styling */
input:invalid + .error-message {
  display: block;
  color: red;
  font-size: 0.8em;
}

Custom Validation Messages (Constraint Validation API)

Override browser's default error messages with custom, user-friendly text.

HTMLRead-only
1
<input type="text" 
       id="username" 
       required 
       minlength="3"
       oninvalid="this.setCustomValidity('Username must be at least 3 characters')"
       oninput="this.setCustomValidity('')">

<script>
// More robust approach using addEventListener
const emailInput = document.querySelector('#email');
emailInput.addEventListener('invalid', (e) => {
  if (emailInput.validity.typeMismatch) {
    emailInput.setCustomValidity('Please enter a valid email address (e.g., name@example.com)');
  } else if (emailInput.validity.valueMissing) {
    emailInput.setCustomValidity('Email address is required');
  }
});

emailInput.addEventListener('input', () => {
  emailInput.setCustomValidity('');
});
</script>

Constraint Validation API Properties

PropertyDescriptionExample Use
`validity.valueMissing`Field is required but emptyShow 'This field is required'
`validity.typeMismatch`Wrong type (email/url format)Show 'Enter a valid email'
`validity.tooShort`Below `minlength`Show minimum character count
`validity.tooLong`Above `maxlength`Show maximum character limit
`validity.rangeUnderflow`Below `min` valueShow minimum value allowed
`validity.rangeOverflow`Above `max` valueShow maximum value allowed
`validity.patternMismatch`Regex pattern failsShow format instructions
`validity.valid`All constraints passEnable submit button

Disabling HTML Validation (When & Why)

HTMLRead-only
1
<!-- Disable validation for entire form -->
<form novalidate>
  <input type="email" required>
  <button type="submit">Submit</button>
</form>

<!-- Disable validation for a single button -->
<form>
  <input type="text" required>
  <button type="submit">Validate</button>
  <button type="submit" formnovalidate>Save Draft (No Validation)</button>
</form>

When to Use HTML Validation vs JavaScript vs Server

Validation LayerBest ForLimitations
**HTML attributes**Basic required, type, length, simple patternsNo async checks, limited regex, browser differences
**JavaScript**Complex rules, async (username availability), real-time hintsCan be bypassed (disable JS in browser)
**Server-side**Security-critical checks, database lookups, business logicSlower feedback, extra network request

⚠️ Critical Security Warning

HTML validation is client-side only — users can bypass it entirely (disable JavaScript, edit HTML via DevTools, or craft custom HTTP requests). Always implement server-side validation for security. Never trust client-side data.

Common Mistakes & How to Fix Them

❌ Mistake✅ Fix
Using `pattern` without `title` attributeAlways add `title` to explain the required format
No server-side validationAlways validate on server — never trust the client
Vague error messages ('Invalid input')Be specific ('Phone number must be 10 digits')
Blocking form submission without explanationUse `:invalid` CSS + custom messages to guide users
Using `alert()` for validation errorsUse inline validation messages near the field

Accessibility Tips

  • Always use <label> linked to inputs via for/id
  • Add aria-describedby pointing to error message container
  • Use aria-invalid="true" when validation fails
  • Don't rely on color alone — add text descriptions for errors
  • Test with screen readers (NVDA, VoiceOver, JAWS)

Complete Working Example

HTMLRead-only
1
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        body { font-family: Arial; max-width: 400px; margin: 50px auto; }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input { width: 100%; padding: 8px; border: 2px solid #ccc; }
        input:invalid { border-color: red; }
        input:valid { border-color: green; }
        .error-hint { color: #666; font-size: 0.8em; display: block; }
        button { background: #007bff; color: white; padding: 10px; border: none; cursor: pointer; }
    </style>
</head>
<body>
    <form action="/submit" method="post" novalidate>
        <div class="form-group">
            <label for="name">Name (required, min 2 letters)</label>
            <input type="text" id="name" name="name" 
                   pattern="[A-Za-z]{2,}" 
                   required
                   title="Only letters, at least 2 characters">
            <span class="error-hint">Only letters, minimum 2 characters</span>
        </div>

        <div class="form-group">
            <label for="email">Email</label>
            <input type="email" id="email" name="email" required>
        </div>

        <div class="form-group">
            <label for="age">Age (18-99)</label>
            <input type="number" id="age" name="age" min="18" max="99" required>
        </div>

        <button type="submit">Submit</button>
    </form>

    <script>
        // Custom validation messages
        const form = document.querySelector('form');
        const nameInput = document.getElementById('name');

        nameInput.addEventListener('invalid', (e) => {
            if (nameInput.validity.patternMismatch) {
                nameInput.setCustomValidity('Please use only letters (A-Z, a-z) with at least 2 characters');
            } else if (nameInput.validity.valueMissing) {
                nameInput.setCustomValidity('Name is required');
            }
        });

        nameInput.addEventListener('input', () => {
            nameInput.setCustomValidity('');
        });
    </script>
</body>
</html>

Conclusion

HTML form validation is a powerful, zero-JavaScript way to improve user input quality. Use required, pattern, min/max, and proper input types for 80% of validation needs. Add CSS pseudo-classes for visual feedback, custom messages for clarity, and always back it up with server-side validation for security.

Try it yourself

<!DOCTYPE html>
<html>
<head>
    <style>
        body { font-family: Arial; padding: 20px; max-width: 400px; margin: auto; }
        input { width: 100%; padding: 8px; margin: 5px 0 15px; border: 2px solid #ccc; }
        input:invalid { border-color: #ff6b6b; background: #fff5f5; }
        input:valid { border-color: #51cf66; }
        button { background: #1c7ed6; color: white; padding: 10px; border: none; cursor: pointer; width: 100%; }
        .hint { font-size: 0.8em; color: #666; }
    </style>
</head>
<body>
    <h2>📝 Register</h2>
    <form>
        <label>Username (3-15 letters/numbers):</label>
        <input type="text" pattern="[A-Za-z0-9]{3,15}" required title="3-15 letters or numbers">
        
        <label>Email:</label>
        <input type="email" required>
        
        <label>Age (18+):</label>
        <input type="number" min="18" max="120" required>
        
        <button type="submit">Submit</button>
        <div class="hint">Try submitting empty or invalid data → browser shows error</div>
    </form>
</body>
</html>

Test Your Knowledge

Q1
of 5

Which HTML attribute enables custom regex-based validation?

A
regex
B
match
C
pattern
D
validate
Q2
of 5

True or false: HTML form validation is sufficient for security.

A
True
B
False
C
Only for login forms
D
Only for search forms
Q3
of 5

Which CSS pseudo-class styles an invalid input field?

A
:error
B
:invalid
C
:wrong
D
:bad
Q4
of 5

What attribute disables browser validation for an entire form?

A
nocheck
B
disable-validation
C
novalidate
D
skip-validation
Q5
of 5

Which JavaScript method customizes browser validation messages?

A
setErrorMessage()
B
setCustomValidity()
C
setValidationMessage()
D
setError()

Frequently Asked Questions

Can HTML validation prevent spam or malicious input?

No — absolutely not. HTML validation is purely for user convenience. Malicious users can bypass it easily. Always validate on the server.

What's the difference between `pattern` and `type="email"`?

type="email" uses a built-in, non-configurable regex for email format. pattern lets you write any custom regex (e.g., only @gmail.com addresses).

Why does my custom validation message disappear on first keystroke?

Because setCustomValidity('') clears it. That's correct behavior — you want the message to clear as the user corrects the input.

Does `pattern` work with `type="number"`?

No. pattern is ignored on type="number". Use type="text" with pattern + inputmode="numeric" for custom number validation.

How do I validate password confirmation with HTML only?

You can't — that requires JavaScript or server-side validation. HTML has no built-in way to compare two fields.

Previous

html input types

Next

html select

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.