HTML form validation can enforce required values, types, lengths, ranges, and patterns before submission. It improves immediate feedback but is not a security boundary: clients can disable it or send requests directly, so the server must repeat every authoritative rule.
HTML5 provides built-in form validation - no JavaScript needed for basic checks. The browser validates fields when the user submits the form and shows error messages automatically.
<form action="/submit" method="POST">
<!-- required: field must not be empty -->
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<!-- minlength / maxlength: character count limits -->
<label for="username">Username (3-20 chars):</label>
<input type="text" id="username" name="username"
minlength="3" maxlength="20" required>
<!-- min / max: numeric range -->
<label for="age">Age (18-100):</label>
<input type="number" id="age" name="age" min="18" max="100" required>
<!-- type="email": validates email format -->
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<!-- type="url": validates URL format -->
<label for="website">Website:</label>
<input type="url" id="website" name="website"
placeholder="https://example.com">
<!-- pattern: custom regex validation -->
<label for="phone">Phone (10 digits):</label>
<input type="tel" id="phone" name="phone"
pattern="[0-9]{10}"
title="Please enter exactly 10 digits">
<!-- pattern: password strength -->
<label for="password">Password:</label>
<input type="password" id="password" name="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
title="Must contain at least 8 characters, one uppercase, one lowercase, one number"
required>
<!-- step: numeric step value -->
<label for="price">Price (multiples of 0.50):</label>
<input type="number" id="price" name="price"
min="0" step="0.50">
<button type="submit">Submit</button>
</form>
| Attribute | Description | Works on |
|---|---|---|
| required | Field must not be empty | All inputs |
| minlength | Minimum number of characters | text, password, textarea |
| maxlength | Maximum number of characters | text, password, textarea |
| min | Minimum value | number, date, range |
| max | Maximum value | number, date, range |
| step | Legal number intervals | number, range, date |
| pattern | Regex pattern the value must match | text, tel, email, url |
| title | Custom error message shown when pattern fails | All inputs |
| type="email" | Validates email format automatically | input |
| type="url" | Validates URL format automatically | input |
| novalidate | Disables all validation on the form | form |
Use CSS pseudo-classes to style valid and invalid fields:
/* Valid input - green border */
input:valid {
border: 2px solid #27ae60;
outline-color: #27ae60;
}
/* Invalid input - red border */
input:invalid {
border: 2px solid #e74c3c;
outline-color: #e74c3c;
}
/* Only show invalid style after user has interacted */
input:not(:placeholder-shown):invalid {
border: 2px solid #e74c3c;
}
/* Required field indicator */
input:required {
border-left: 4px solid #f39c12;
}
<form>
<input type="email" placeholder="Enter email" required>
<!-- Green border when valid, red when invalid -->
<button type="submit">Submit</button>
</form>
const emailInput = document.getElementById('email');
emailInput.addEventListener('invalid', () => {
if (emailInput.validity.valueMissing) {
emailInput.setCustomValidity('Please enter your email address.');
} else if (emailInput.validity.typeMismatch) {
emailInput.setCustomValidity('Please enter a valid email like: name@example.com');
} else {
emailInput.setCustomValidity('');
}
});
// Clear custom message when user starts typing
emailInput.addEventListener('input', () => {
emailInput.setCustomValidity('');
});
Use a real label, helpful instructions, and constraints that match the server contract. The pattern attribute uses full-value matching behavior and should not replace dedicated input types. Avoid placeholder-only labels and do not encode complex business rules in unreadable patterns.
On submission, report field errors near their controls and in a concise summary when useful, set aria-invalid for custom handling, and move focus deliberately. novalidate is appropriate only when the application supplies an equivalent accessible validation experience.
<section aria-labelledby="html5-title">
<h2 id="html5-title">HTML5</h2>
<p>This block uses meaningful tags so browsers, users, and assistive technology understand the content.</p>
<a href="/learn/html5">Read the full HTML5 note</a>
</section>
<form>
<label for="html5-input">HTML5 value</label>
<input id="html5-input" name="html5" required>
<button type="submit">Save</button>
</form>
<!-- Check: every interactive element has a name, purpose, and keyboard path. -->
No. Browser checks improve usability, but requests can bypass them.
It rejects an empty supported form control, but it does not prove the value is correct.
Use it for a simple text format, then repeat the rule on the server.
Explore 500+ free tutorials across 20+ languages and frameworks.