EthicsComputerCourses
Join training +91 90532 10052

Form Validation Plugins

jQuery · Lesson 21 of 36 Intermediate 6 min read Updated

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:

  1. Load jQuery, then the plugin file jquery.validate.min.js (and additional-methods.min.js if you need extra rules).
  2. Call .validate() once on the form. Rules are keyed by each field's name attribute, not its id.
  3. Add friendly messages for each rule.
  4. Add custom rules with $.validator.addMethod().
  5. Put your AJAX code in submitHandler; it runs only when every rule passes.
HTMLTry it yourself
<!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:

HTML
<!-- 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

CodeWhat 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.

HTMLTry it yourself
<!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.

NeedjQuery ValidationBuilt in
Required, length, email, number rangerules or HTML attributesthe same HTML attributes
Custom format$.validator.addMethod()pattern or setCustomValidity()
Check with the server while typingremoteyour 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 id in rules. Rules are keyed by name.
  • 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 with rules, messages and a submitHandler; 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
JavaScript
$.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?

2. Rules in the rules object are matched to fields by which attribute?

3. A developer calls $("#f").validate({ rules: { city: { required: true } } }) a second time to add a rule. What happens?

4. Which native feature replaces a custom regex rule in many cases?

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