Form Validation Plugins
Open the admissions form, the GST invoice screen or the HR leave form of almost any company site built before 2020 and you will probably find $("#form").validate({...}). That is the jQuery Validation Plugin, the most widely used validation library of the jQuery era. Some server frameworks even generate code for it: older ASP.NET MVC projects, for example, use Microsoft's jquery.validate.unobtrusive add-on, which reads data-val-* attributes written by the server.
This lesson is an overview for maintainers: how the plugin is wired up, how to read and change its rules, how to add your own rule, and how the same checks look with the browser's built-in validation. It is not a full manual; the official documentation at jqueryvalidation.org lists every option.
First rule: the browser is not the guard
Client-side validation (in the browser) is for comfort: it tells Priya that her mobile number has nine digits before she waits for the server. It is not security. Anyone can switch off JavaScript, edit the page in DevTools or send a request with a tool like curl. The server must check every field again, every time. When you maintain a form, check that every rule in the JavaScript also exists on the server.
The plugin in five steps
The current release, 1.22.1 (February 2026), lists jQuery 4 among its supported versions, so it keeps working after an upgrade to jQuery 4. Here is how a typical form is set up:
- Load jQuery, then the plugin file
jquery.validate.min.js(andadditional-methods.min.jsif you need extra rules). - Call
.validate()once on the form. Rules are keyed by each field'snameattribute, not itsid. - Add friendly
messagesfor each rule. - Add custom rules with
$.validator.addMethod(). - Put your AJAX code in
submitHandler; it runs only when every rule passes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery Validation plugin</title>
<style>
form { display: grid; gap: 0.3rem; max-inline-size: 22rem; }
label.error { color: #b3261e; font-size: 0.9rem; }
input.error { border-color: #b3261e; }
</style>
</head>
<body>
<form id="admission">
<label for="name">Student name</label>
<input id="name" name="name" value="Ro">
<label for="mobile">Parent's mobile</label>
<input id="mobile" name="mobile" inputmode="numeric" value="512345678">
<label for="pin">PIN code</label>
<input id="pin" name="pin" inputmode="numeric" value="12403">
<label for="email">Email (optional)</label>
<input id="email" name="email" value="rohan@">
<button>Apply</button>
</form>
<p id="result"></p>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.22.1/dist/jquery.validate.min.js"></script>
<script>
// Step 4: custom rules (this.optional lets empty optional fields pass)
$.validator.addMethod("indianMobile", function (value, element) {
return this.optional(element) || /^[6-9]\d{9}$/.test(value);
}, "Enter a 10-digit mobile number starting with 6, 7, 8 or 9.");
$.validator.addMethod("pincode", function (value, element) {
return this.optional(element) || /^[1-9]\d{5}$/.test(value);
}, "A PIN code has 6 digits and does not start with 0.");
// Steps 2, 3 and 5
$("#admission").validate({
rules: {
name: { required: true, minlength: 3 },
mobile: { required: true, indianMobile: true },
pin: { required: true, pincode: true },
email: { email: true }, // optional, but checked if filled
},
messages: {
name: { required: "Please enter the student's name.", minlength: "At least 3 letters, please." },
},
submitHandler: function (form) {
// A real page would send $(form).serialize() with $.post() here
$("#result").text("All good: sending application for " + $("#name").val());
},
});
</script>
</body>
</html>Press Apply: each field shows its own message. Fix the fields one by one and watch the messages disappear as you type. Behind the scenes the plugin adds novalidate to the form (so the browser's own bubbles do not appear as well), gives failing fields the class error and aria-invalid="true", and inserts each message as a <label class="error"> with an id such as mobile-error.
Rules from HTML attributes
You do not always find rules in the JavaScript. The plugin also reads them from the HTML, which is why a form can be validated by a bare $("form").validate() call:
<!-- Standard HTML attributes become rules -->
<input name="email" type="email" required>
<input name="age" type="number" min="18" max="60">
<input name="city" minlength="3">
<!-- data-rule-* and data-msg-* set rules and messages per field -->
<input name="gstin" data-rule-minlength="15" data-rule-maxlength="15"
data-msg-minlength="A GSTIN has exactly 15 characters.">When a field is validated "for no reason", look in three places: the rules object, the field's HTML attributes, and any $.validator.addClassRules() or .rules("add", ...) calls elsewhere in the code.
Methods you will use when maintaining forms
| Code | What it does |
|---|---|
$("#f").valid() | Validates now and returns true or false, for example before a multi-step "Next" button |
$("#mobile").rules("add", { minlength: 10 }) | Adds a rule to one field at runtime, for fields added by JavaScript |
$("#f").validate().resetForm() | Clears messages and error classes, for example when a popup form is reopened |
remote: "/api/check-username" | Asks the server; the response true means valid, false or a message string means invalid |
ignore: ":hidden" | The default: hidden fields are skipped, which surprises people with tabbed forms |
The modern way: built-in constraint validation
Browsers have validation built in: required, type="email", pattern, minlength, min and max. The Constraint Validation API lets you add custom messages with setCustomValidity(), and the :user-invalid CSS selector styles a field only after the user has interacted with it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Native validation</title>
<style>
form { display: grid; gap: 0.3rem; max-inline-size: 22rem; }
input:user-invalid { border-color: #b3261e; outline-color: #b3261e; }
.msg { color: #b3261e; font-size: 0.9rem; min-block-size: 1.2em; }
</style>
</head>
<body>
<form id="admission" novalidate>
<label for="mobile">Parent's mobile</label>
<input id="mobile" name="mobile" required pattern="[6-9][0-9]{9}" inputmode="numeric"
aria-describedby="mobile-msg" value="512345678">
<p class="msg" id="mobile-msg"></p>
<label for="pin">PIN code</label>
<input id="pin" name="pin" required pattern="[1-9][0-9]{5}" inputmode="numeric"
aria-describedby="pin-msg" value="12403">
<p class="msg" id="pin-msg"></p>
<button>Apply</button>
</form>
<p id="result"></p>
<script type="module">
const form = document.querySelector("#admission");
const texts = {
mobile: "Enter a 10-digit mobile number starting with 6, 7, 8 or 9.",
pin: "A PIN code has 6 digits and does not start with 0.",
};
function check(input) {
const msg = document.getElementById(input.name + "-msg");
msg.textContent = input.validity.valid ? "" : (input.validity.valueMissing ? "Required." : texts[input.name]);
input.setAttribute("aria-invalid", String(!input.validity.valid));
return input.validity.valid;
}
form.addEventListener("input", (event) => check(event.target));
form.addEventListener("submit", (event) => {
event.preventDefault();
const results = [...form.elements].filter((el) => el.name).map(check);
document.querySelector("#result").textContent =
results.every(Boolean) ? "All good: sending application." : "Please fix the fields above.";
});
</script>
</body>
</html>The form uses novalidate so that our messages replace the browser's bubbles, but the rules themselves (required, pattern) are still the browser's. Remove novalidate and the code for a zero-JavaScript version that still blocks bad submits. For more, see Forms & Serialization.
| Need | jQuery Validation | Built in |
|---|---|---|
| Required, length, email, number range | rules or HTML attributes | the same HTML attributes |
| Custom format | $.validator.addMethod() | pattern or setCustomValidity() |
| Check with the server while typing | remote | your own debounced fetch() |
| Validate now | .valid() | form.checkValidity() / form.reportValidity() |
Common mistakes
- Trusting client-side checks. The server must validate everything again.
- Using the field
idinrules. Rules are keyed byname. - Calling
.validate()twice to change rules. The second call is ignored; use.rules("add"). - Submitting with a click handler instead of
submitHandler. The AJAX request is then sent even when the form is invalid. - Forgetting
this.optional(element)in custom methods. Optional fields then fail when empty. - Hidden required fields in tabbed forms. They are ignored by default, so an incomplete form can pass.
Key points
- The jQuery Validation Plugin (1.22.1) is common in legacy forms and supports jQuery 4.
- Call
.validate()once withrules,messagesand asubmitHandler; rules use field names. - Rules also come from HTML attributes and
data-rule-*/data-msg-*attributes. $.validator.addMethod()adds custom rules such as Indian mobile numbers and PIN codes.- Native validation (
required,pattern,setCustomValidity(),:user-invalid) covers most needs without a plugin. - Client-side validation improves comfort; only server-side validation protects data.
Exercise
Add a PAN number field to the plugin example. A PAN has 5 capital letters, 4 digits and 1 capital letter, for example ABCPK1234F. Write a pan method with $.validator.addMethod(), make the field required, and convert the value to capitals as the user types so that abcpk1234f is accepted.
Show solution
$.validator.addMethod("pan", function (value, element) {
return this.optional(element) || /^[A-Z]{5}[0-9]{4}[A-Z]$/.test(value);
}, "Enter a valid PAN, for example ABCPK1234F.");
$("#pan").on("input", function () {
this.value = this.value.toUpperCase();
});
// In the rules object passed to .validate():
// pan: { required: true, pan: true },Add <label for="pan">PAN</label><input id="pan" name="pan"> to the form. The input handler changes the value to capitals before the plugin checks it, so the regular expression only needs to accept capital letters.
Quick Quiz
1. Why must the server validate a form even when the jQuery Validation Plugin checks it?
Client-side checks are for user comfort; only the server can enforce rules.
2. Rules in the rules object are matched to fields by which attribute?
The plugin uses the name attribute, the same one that is sent to the server.
3. A developer calls $("#f").validate({ rules: { city: { required: true } } }) a second time to add a rule. What happens?
Use $("#city").rules("add", { required: true }) to add rules later.
4. Which native feature replaces a custom regex rule in many cases?
Browsers check pattern themselves and expose the result through the Constraint Validation API.
Learn jQuery with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
