Free Online Code Editor
Validate forms with plain HTML attributes, style valid and invalid states with CSS, and use the Constraint Validation API for custom messages.
Before you reach for a validation library, know that browsers already do a lot of the work. With a few HTML attributes you get error bubbles, blocked submissions, and the right keyboard on phones, with no JavaScript at all.
One rule first: client-side validation is for convenience, not security. Anyone can bypass it. Your server must check everything again. The browser checks are there to help honest users fix mistakes quickly.
The type attribute tells the browser what kind of data to expect. It validates the format and shows the best keyboard on mobile.
<form>
<label>Email <input type="email" name="email" required></label><br><br>
<label>Website <input type="url" name="site"></label><br><br>
<label>Age <input type="number" name="age" min="13" max="120"></label><br><br>
<button>Send</button>
</form>Try submitting with a bad email. The browser blocks the form and points at the problem. email, url, number, date, and tel are all worth using.
required means the field can't be empty.minlength / maxlength limit the length of text.min / max limit numbers and dates.step controls which numbers are allowed (for example step="5").pattern checks the value against a regular expression.<form>
<label>Username
<input name="user" required minlength="3" maxlength="16"
pattern="[a-z0-9_]+" title="Lowercase letters, numbers, and underscores only">
</label>
<br><br>
<label>Password
<input type="password" name="pw" required minlength="8">
</label>
<br><br>
<button>Create account</button>
</form>Always add a title when you use pattern. Browsers show it as part of the error message, which tells people what the rule actually is. A pattern is automatically anchored to the whole value, so you don't need ^ and $.
Every input needs a visible label. It improves accessibility, and clicking the label focuses the field, which helps on touch screens. Wrapping the input in the label, as above, is the simplest way. You can also use for and id:
<label for="city">City</label>
<input id="city" name="city" type="text" autocomplete="address-level2">Add the correct autocomplete value, too. Browsers can then fill in names, addresses, and cards, and completion rates go up.
CSS has pseudo-classes for validation. There's a catch, though: if you style :invalid directly, empty required fields turn red before the user has touched anything. Use :user-invalid, which only applies after interaction.
<style>
input { padding: 8px; border: 2px solid #ccc; border-radius: 6px; }
input:user-invalid { border-color: #dc2626; background: #fef2f2; }
input:user-valid { border-color: #16a34a; }
</style>
<form>
<label>Email <input type="email" required></label>
<button>Go</button>
</form>Type something wrong, then click away from the field. The border changes only after you've had a chance to fill it in.
The default messages are plain. For better wording, use JavaScript. Every field has a validity object that tells you exactly what's wrong, and setCustomValidity() lets you replace the message.
<form id="signup">
<label>Age <input id="age" type="number" min="18" max="99" required></label>
<p id="msg" style="color:#b91c1c"></p>
<button>Join</button>
</form>
<script>
const age = document.querySelector("#age");
age.addEventListener("input", () => {
age.setCustomValidity(""); // clear old message first
if (age.validity.rangeUnderflow) {
age.setCustomValidity("You need to be at least 18 to join.");
} else if (age.validity.valueMissing) {
age.setCustomValidity("Please tell us your age.");
}
});
document.querySelector("#signup").addEventListener("submit", (event) => {
event.preventDefault();
document.querySelector("#msg").textContent = "Submitted! (demo only)";
});
</script>You must reset the message to an empty string when the value becomes valid again. Otherwise the field stays stuck as invalid.
The validity object has flags such as valueMissing, typeMismatch, patternMismatch, tooShort, rangeUnderflow, and rangeOverflow. Check the one you care about and respond with a specific message.
Browser bubbles can't be styled. If you want errors in your own design, turn off the default behavior with novalidate and validate in your own handler:
<form id="f" novalidate>
<label>Email <input id="email" type="email" required></label>
<span id="err" style="color:#b91c1c"></span>
<button>Subscribe</button>
</form>
<script>
const form = document.querySelector("#f");
const email = document.querySelector("#email");
const err = document.querySelector("#err");
form.addEventListener("submit", (event) => {
event.preventDefault();
if (email.checkValidity()) {
err.textContent = "";
alert("Thanks!");
} else {
err.textContent = email.validationMessage;
}
});
</script>checkValidity() returns true or false, and validationMessage gives you the browser's text, which you can then show wherever you like.
Good error messages are specific and polite. Compare "Invalid input" with "Use 8 or more characters." Say what's wrong, then say how to fix it. Don't rely on color alone, since some users can't tell red from green. Add text or an icon, and use aria-describedby to connect error text to its field for screen readers.
pattern for emails. Use type="email". Email rules are too complicated for a short regex.setCustomValidity message will block submission.type and attributes first. They cover most needs.:user-invalid, not :invalid.setCustomValidity and validity give you friendly, custom messages.Reading helps, but typing the code is what makes it stick. Open the editor, paste any example from this article, and change it until it breaks.
Open the HTML/CSS editorStop guessing between Flexbox and CSS Grid. Learn the one question that decides it, with copy-and-run layouts for navbars, card galleries, and page templates.
Learn when to use CSS transitions and when to use keyframe animations, which properties stay smooth, and how to respect users who prefer reduced motion.
Build pages that work on phones, tablets, and desktops. Covers the viewport tag, clamp(), mobile-first media queries, flexible images, and testing.