Tracking AJAX Form Submissions with Google Tag Manager: A Step-by-Step Guide

If you regularly use Google Tag Manager (GTM) to set up event tracking for form submissions, you've likely encountered forms that submit using AJAX, which can make capturing successful form submissions challenging with standard methods. Unlike traditional form submissions, AJAX forms do not trigger the typical submit events, making it difficult to track them using straightforward GTM configurations. I encountered this problem frequently, and eventually developed a reliable, easy-to-implement solution that works consistently for AJAX-based forms. By using this approach, you can ensure that form submissions are tracked properly, allowing you to gather accurate data for your analytics. This solution is particularly helpful for marketers and developers who rely heavily on GTM for comprehensive event tracking. Here's a step-by-step guide to implementing it:

Step 1: Identify the Form Submission URL

First, we need to determine the URL to which the form is being submitted. This URL is typically where the AJAX request sends the form data. You can do this using your browser's developer tools—in this example, I'll use Chrome. Open the Network tab, submit the form, and locate the URL where the AJAX script sends the data. In my case, it's ajax.php, and I'll use this URL in my JavaScript code. By identifying this URL, we can set up a script to listen for when a request is made to it, allowing us to capture the submission event.

Step 2: Create a Custom HTML Tag in GTM

Next, create a new Custom HTML Tag in GTM and insert the following JavaScript. This tag should be configured to fire on the page where the form is present. To simplify the setup, I configured it to fire on All pages. This ensures that the script is always active on pages where the form may be present, making it easier to manage in the long run. GTM Tag Name: Form Request Listener Trigger: All pages

<script>
(function() {
  var originalFetch = window.fetch;
  window.fetch = function(input, init) {
    // Call the original fetch function
    var promise = originalFetch.apply(this, arguments);

    // Check if the request URL contains 'ajax.php'
    if (typeof input === 'string' && input.includes('ajax.php')) {
      promise.then(function(response) {
        // Check if the request was successful (status in the range 200-299)
        if (response.ok) {
          // Assuming {{Form Data Extractor}} returns relevant form data
          var formData = {{Form Data Extractor}}();
          if (formData) {
            window.dataLayer.push({
              event: 'formSubmission',
              formData: formData
            });
            console.log('Form submission was successful:', formData);
          }
        } else {
          console.log('Form submission failed with status:', response.status);
        }
      }).catch(function(error) {
        // Handle network errors or request failures
        console.error('Form submission encountered an error:', error);
      });
    }

    return promise;
  };

  var originalXHR = window.XMLHttpRequest.prototype.send;
  window.XMLHttpRequest.prototype.send = function(data) {
    var xhr = this;
    var oldOnReadyStateChange = xhr.onreadystatechange;
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        if (xhr.responseURL && xhr.responseURL.includes('ajax.php')) {
          var formData = {{Form Data Extractor}}();
          if (formData) {
            window.dataLayer.push({
              event: 'formSubmission',
              formData: formData
            });
            console.log('Form submission detected (XHR):', formData);
          }
        }
      }
      if (oldOnReadyStateChange) {
        oldOnReadyStateChange.apply(this, arguments);
      }
    };
    return originalXHR.apply(this, arguments);
  };
})();
</script>
  
This script works by overriding the default behavior of both the fetch API and the XMLHttpRequest object. It monitors network requests made by the page, and if any request is made to the URL containing ajax.php, it checks whether the request was successful. If it was, the script pushes the form data to the dataLayer, allowing you to track the form submission in GTM.

Step 3: Extract Form Data

To extract form data and send it to GA4, you can implement a Custom JavaScript Variable. In my case, I wanted to extract data from a form with the ID request-showing. This step is crucial because it allows you to capture specific data points from the form, such as the user's email, phone number, and other input fields that are relevant to your analytics goals. GTM Variable (Custom JavaScript): Form Data Extractor

function() {
  return function() {
    var form = document.getElementById('request-showing');
    if (form) {
      return {
        formemail: form.querySelector('[name="formemail"]') ? form.querySelector('[name="formemail"]').value : '',
        formphone: form.querySelector('[name="formphone"]') ? form.querySelector('[name="formphone"]').value : '',
        formdate: form.querySelector('[name="formdate"]') ? form.querySelector('[name="formdate"]').value : '',
        formmessage: form.querySelector('[name="formmessage"]') ? form.querySelector('[name="formmessage"]').value : '',
        formsubject: form.querySelector('[name="formsubject"]') ? form.querySelector('[name="formsubject"]').value : '',
      };
    }
    return null;
  };
}
ICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2hpZ2hsaWdodC5qcy8xMS41LjEvc3R5bGVzL2RlZmF1bHQubWluLmNzcyI+CiAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2hpZ2hsaWdodC5qcy8xMS41LjEvaGlnaGxpZ2h0Lm1pbi5qcyI+PC9zY3JpcHQ+IAo8c2NyaXB0PmhsanMuaGlnaGxpZ2h0QWxsKCk7PC9zY3JpcHQ+ In GTM, Custom JavaScript Variables are useful for performing complex data extraction or calculations. The script above defines a function that extracts form values from a specified form element. This function runs whenever a GTM tag or trigger references this variable, such as during form submissions. By using this approach, you can ensure that you capture the specific data you need from the form, which can be incredibly valuable for analytics and marketing automation purposes. For example, you can use this form data to create more personalized marketing campaigns or to track the effectiveness of different form fields in driving conversions. The extracted data can also be used for retargeting purposes, allowing you to create audiences based on users who submitted specific forms.

Step 4: Set Up a Trigger for the 'formSubmission' Event

Create a custom event trigger in GTM and set the Event Name parameter to 'formSubmission'. You can now attach any tags to this trigger to send events, with or without form data, to GA4, Facebook, or other analytics platforms. This trigger is the key to connecting the form submission event with your various tracking tags, enabling you to properly capture and analyze user behavior. For example, if you want to send form submission data to Google Analytics 4 (GA4), you can create a GA4 event tag that fires whenever the 'formSubmission' event occurs. Similarly, you can use this trigger to send data to Facebook for conversion tracking, allowing you to measure the effectiveness of your advertising campaigns. This setup provides a robust way to track AJAX form submissions and ensure that you are capturing all relevant data. It not only allows you to track whether a form was successfully submitted but also gives you the flexibility to extract detailed form data, which can be crucial for optimizing your marketing strategies. By implementing these steps, you'll have greater visibility into user interactions on your website, leading to more informed decision-making and better overall campaign performance.
Share
Copied!

Related posts

Your SaaS Stack Works Fine. Your Workflow Doesn't.

The average small or mid-sized business runs somewhere between 25 and 50 SaaS tools. Everyone knows this number is growing. What gets less attention is what actually happens inside the company that uses all of them - and why CRM workflow automation remains broken even when every individual tool works fine. Take a real scenario. […]

Yury Parfentsov ·
WooCommerce Slow Again

Why WooCommerce Stores Keep Getting Slower (And What Actually Fixes It)

If you've run a WooCommerce store long enough, you know this cycle: the site feels fast at launch, you optimize a few things, performance improves—then traffic grows and everything slows down again.
You compress images. Minify CSS. Install a caching plugin. Upgrade hosting. And six months later, you're back where you started.
This isn't because WooCommerce is broken. It's because most performance advice targets the wrong layer of your system.

Yury Parfentsov ·
website form security

When Spam Looks Like Random Letters: Website Form Security in 2025

A new wave of automated spam has emerged — one that looks harmless but signals a shift in how bots operate. These form submissions, filled with random strings of letters instead of links or sales pitches, are not typical spam but automated probes testing how websites handle validation. This article explores the growing trend of “intelligent” form bots that mimic legitimate user behavior, bypass traditional validation logic, and challenge our assumptions about how spam operates — and how we should defend against it.

Yury Parfentsov ·

Leave a comment

Your email address will not be published. Required fields are marked *