# jQuery submit form with validation

Submit a form with jQuery AJAX and check it first with the jQuery Validation plugin. Both samples ran in Chromium with jQuery 4.0.0.

Author: Formgong
Published: 2026-11-03
Language: en
Canonical: https://formgong.com/en/blog/jquery-submit-form-validation/

Catch the form's submit event and call event.preventDefault() so the page does not reload. Then send the fields with $.ajax and $(form).serialize(). To check the fields first, put that send inside the jQuery Validation plugin's submitHandler. In our 11 test cases with jQuery 4.0.0, the plugin stopped 2 of 3 broken forms, and the server's field check caught the third.

## Why does a plain form submit lose the visitor?

A normal HTML form sends the visitor away. The browser posts the fields and loads whatever page the server answers with. If that is a bare JSON reply or an error page, the visitor thinks the message was lost and often sends it again. Submitting with jQuery keeps them on your page, and you decide what they see.

The two searches people make, how to submit a form with jQuery and how to validate it, are two halves of one job, so this article does both. First the send on its own, then the same send behind the Validation plugin. Both ran against a local copy of Formgong's form backend, which answers JSON when the request asks for it.

## How do you submit a form with jQuery without reloading the page?

Listen for `submit` on the form, not `click` on the button, so the Enter key goes through the same code. `event.preventDefault()` stops the browser's own post. `$(form).serialize()` turns the fields into the same string the browser would have sent, so the backend sees no difference.

The `Accept: application/json` header asks the backend for JSON instead of a redirect, and `dataType: "json"` tells jQuery to parse it. Disabling the button while the request runs is what turned a double click into one request in our test. On failure the sample shows the server's own message when there is one, so “That access key does not exist.” reaches you during setup instead of a generic error.

The jQuery documentation marks the `.submit(handler)` shorthand as deprecated since 3.3 and points to `.on("submit", handler)`. It still exists in jQuery 4.0.0, we checked, but the long form is the one that will stay.

```javascript
$("#contact").on("submit", function (event) {
  event.preventDefault(); // stay on the page
  var form = this;
  var $button = $(form).find("button[type=submit]");
  if ($button.prop("disabled")) return; // one send at a time
  $button.prop("disabled", true).text("Sending…");
  $.ajax({
    url: form.action,
    method: "POST",
    data: $(form).serialize(),
    dataType: "json",
    headers: { Accept: "application/json" }
  })
    .done(function () { form.reset(); $("#status").text("Thanks, your message was sent."); })
    .fail(function (xhr) {
      var data = xhr.responseJSON || {};
      $("#status").text(data.message || "Not sent: check your connection and try again.");
    })
    .always(function () { $button.prop("disabled", false).text("Send"); });
});
```

## How do you validate the form before it sends?

The jQuery Validation plugin checks each field against its rules and puts a message next to the broken ones. It calls `submitHandler` only when every rule passes. So the send from the first sample moves into `submitHandler`, and nothing goes out while a field is wrong.

Two details come from jQuery 4. It removed `$.trim`, so the sample passes a small `trim` function to the plugin's `normalizer` option, which is what makes a name of spaces count as empty. And because the server can refuse fields too, the failure branch hands the server's field list to `validator.showErrors()`, so those messages land in the same place as the plugin's own.

```html
<form id="contact" action="https://formgong.com/submit" method="POST" novalidate>
  <input type="hidden" name="access_key" value="fk_your_access_key">
  <input type="text" name="botcheck" tabindex="-1" autocomplete="off" style="display:none">

  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name">

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email">

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="5"></textarea>

  <button type="submit">Send</button>
  <p id="status" role="status"></p>
</form>
<script src="/js/jquery.min.js"></script>
<script src="/js/jquery.validate.min.js"></script>
<script>
$(function () {
  var $form = $("#contact");
  var $status = $("#status");
  var $button = $form.find("button[type=submit]");
  var trim = function (value) { return value.trim(); }; // $.trim is gone in jQuery 4

  function send(form, validator) {
    $button.prop("disabled", true).text("Sending…");
    $status.text("");
    $.ajax({
      url: form.action,
      method: "POST",
      data: $(form).serialize(),
      dataType: "json",
      headers: { Accept: "application/json" }
    })
      .done(function () {
        form.reset();
        $status.text("Thanks, your message was sent.");
      })
      .fail(function (xhr) {
        var data = xhr.responseJSON || {};
        if (validator && Array.isArray(data.errors)) {
          var shown = {};
          data.errors.forEach(function (error) {
            shown[error.field] = error.code === "required" ? "Please fill this in." : "This is not a valid " + error.code + ".";
          });
          validator.showErrors(shown);
          validator.focusInvalid();
        }
        $status.text(data.message || (xhr.status ? "Not sent (error " + xhr.status + "). Please try again." : "Not sent: check your connection and try again."));
      })
      .always(function () {
        $button.prop("disabled", false).text("Send");
      });
  }

  var validator = $form.validate({
    rules: {
      name: { required: true, normalizer: trim },
      email: { required: true, email: true, normalizer: trim },
      message: { required: true, minlength: 10, normalizer: trim }
    },
    messages: {
      name: "Please enter your name.",
      email: { required: "Please enter your email.", email: "Check the email, for example emma@example.com." },
      message: { required: "Please write your message.", minlength: "Please write at least 10 characters." }
    },
    submitHandler: function (form) {
      if (!$button.prop("disabled")) send(form, validator);
    }
  });
});
</script>
```

## What happened when we ran both samples?

The two samples below in Chromium with jQuery 4.0.0 and jquery-validation 1.22.1, posting to a local Formgong endpoint, 3 November 2026.
CaseWhat happened
Plugin: empty form3 messages, 0 requests
Plugin: spaces as the name, `emma@`, “Hi”3 messages, 0 requests
Plugin: `emma@example` as the emailNo message; 1 request, 200, stored as typed
Plugin, form with field checks on the server: `emma@example`1 request, 422; “This is not a valid email.” under the field, focus on it
Plugin: a correct form1 request, 200, form cleared
Plugin: a correct form, double click1 request
Plain submit: empty form1 request, 200, an empty message stored and emailed
Plain submit: a correct form, double click1 request
Plain submit: wrong access key1 request, 404, “That access key does not exist.”, fields kept
Plugin: wrong access key1 request, 404, same message, fields kept
Plugin: browser offline“Not sent: check your connection”, fields kept
Two rows deserve a second look. The plugin's `email` rule accepted `emma@example`, an address with no dot after the `@`, because it follows the same rule as the browser's `type="email"`. The form sent it, and the backend stored an address you cannot write back to. On the form with server-side field checks the same input came back as a `422`, and the plugin showed the server's message under the field.

The plain submit, with no plugin, posted a completely empty form and the backend accepted it, because that form had no field checks. Without validation somewhere, empty messages reach your inbox. If yours already do, our guide to a [form that is not submitting](/en/blog/html-form-not-submitting/) and the one on [duplicate submissions](/en/blog/stop-duplicate-form-submissions/) cover the neighbouring problems.

## Do you still need jQuery for this?

If your site already loads jQuery, use it. WordPress ships it with most themes, and our WooCommerce demo store loads jQuery 3.7.1 on every page without anyone asking for it. Adding plain `fetch()` code next to it gains you nothing.

If jQuery is not on the page, think twice before adding it for one form. jQuery 4.0.0 and the plugin come to about 35 KB compressed (27 KB and 8 KB with gzip). The browser's own `fetch` and `FormData` do the same job, and our guide to an [HTML form action](/en/blog/html-form-action/) shows the plain version. The validation advice stays the same either way. Trim the values and check the email properly, and leave the last word to the server.

## How does this work with Formgong?

Point the form's `action` at `https://formgong.com/submit` and put your access key in a hidden field, as in the samples. Formgong answers JSON to the `Accept` header, so `.done()` and `.fail()` work as shown, and the message reaches your email and Telegram. The hidden `botcheck` field is a honeypot; our [honeypot guide](/en/blog/honeypot-form-field/) explains how it filters bots without a puzzle.

Under Settings → Protection → Field checks you can make Formgong check fields on its side, on every plan including Free. A post that breaks a check gets a `422` with the name of each field, which is the list the sample passes to `showErrors()`. Formspree's jQuery Validate guide, read on 3 November 2026, maps Formspree's server errors the same way, so this is not unique to us. Splitforms' jQuery validation guide describes no field check by its own service. The [HTML forms docs](/en/docs/html/) list the fields Formgong reads.

## Did your jQuery form do something else?

If one of these samples behaves differently on your site, or you run an older jQuery and something breaks, tell us at [support@formgong.com](mailto:support@formgong.com). We will test your version and add it to the table.

## Frequently asked questions

### How do I submit a form with jQuery without reloading the page?

Handle the submit event with .on("submit"), call event.preventDefault(), and send $(form).serialize() with $.ajax.

### Is .submit() deprecated in jQuery?

The .submit(handler) shorthand has been deprecated since jQuery 3.3. Use .on("submit", handler) and .trigger("submit") instead.

### Why does the jQuery Validation email rule accept emma@example?

It follows the same rule as the browser's email field, which allows an address without a dot. Check it again on the server.

### How do I show server errors with jQuery Validation?

Read the field names from the server's answer and pass them to validator.showErrors() as an object of field name and message.

## Sources and documentation

- [Formgong: HTML form not submitting](https://formgong.com/en/blog/html-form-not-submitting/)
- [Formgong: Honeypot form field](https://formgong.com/en/blog/honeypot-form-field/)
- [HTML forms](https://formgong.com/en/docs/html/)

[Get a form key](https://formgong.com/en/#top)
