Validation Should Help, Not Annoy
Good UX makes forms easy to complete.
When to Validate
On blur (leaving field): Best for most fields On submit: Always, as final check On input: Only for specific cases (character count)
// On blur
input.addEventListener('blur', () => validateField(input));
// On submit
form.addEventListener('submit', (e) => {
if (!validateForm()) {
e.preventDefault();
}
});
Show Errors Near Fields
<div class="field">
<label for="email">Email</label>
<input type="email" id="email" aria-describedby="email-error">
<span id="email-error" class="error" role="alert">
Please enter a valid email address
</span>
</div>
.error {
color: #dc2626;
font-size: 0.875rem;
margin-top: 0.25rem;
}
Be Specific
// Bad
"Invalid input"
// Good
"Email must include @ symbol"
"Password must be at least 8 characters"
"Phone number should be 10 digits"
Show Requirements Upfront
<input type="password" id="password">
<ul class="requirements">
<li data-requirement="length">At least 8 characters</li>
<li data-requirement="uppercase">One uppercase letter</li>
<li data-requirement="number">One number</li>
</ul>
Inline Success
.field.valid input {
border-color: #16a34a;
}
.field.valid::after {
content: "✓";
color: #16a34a;
}
Don't Block Typing
// Bad - prevents input
input.addEventListener('keypress', (e) => {
if (!/\d/.test(e.key)) e.preventDefault();
});
// Better - validate after
input.addEventListener('blur', () => {
if (!/^\d+$/.test(input.value)) {
showError('Please enter numbers only');
}
});
Preserve Input
After validation error:
- Keep what user typed
- Focus the first error field
- Scroll to error if needed
