Modern websites don’t always reload after a form is submitted. Instead, they use AJAX LISTENER (Asynchronous JavaScript and XML) to send data in the background. While this improves user experience, it creates a challenge for tracking—because Google Tag Manager’s default form triggers often fail.
If you’ve ever struggled with tracking forms that don’t redirect to a thank-you page, this guide is exactly what you need.
What Is AJAX and Why It Breaks Tracking?
AJAX allows forms to submit data without refreshing the page.
Problem:
GTM’s built-in Form Submission Trigger relies on page reload or standard form behavior.
Result:
- No trigger fires
- No conversion is recorded
- You lose valuable data
Solution: AJAX Listener in GTM
An AJAX listener captures background network requests and pushes them into the dataLayer, allowing GTM to track them as events.
Step-by-Step Setup
1. Add AJAX Listener Code (Advanced jQuery Listener)
Go to Google Tag Manager → Tags → New → Custom HTML Paste this full AJAX listener code:
<script id="gtm-jq-ajax-listen" type="text/javascript"> (function() { 'use strict'; var $; var n = 0; init(); function init(n) { if (typeof jQuery !== 'undefined') { $ = jQuery; bindToAjax(); } else if (n < 20) { n++; setTimeout(init, 500); } } function bindToAjax() { $(document).bind('ajaxComplete', function(evt, jqXhr, opts) { var fullUrl = document.createElement('a'); fullUrl.href = opts.url; var pathname = fullUrl.pathname[0] === '/' ? fullUrl.pathname : '/' + fullUrl.pathname; var queryString = fullUrl.search[0] === '?' ? fullUrl.search.slice(1) : fullUrl.search; var queryParameters = objMap(queryString, '&', '=', true); var headers = objMap(jqXhr.getAllResponseHeaders(), '\n', ':'); dataLayer.push({ 'event': 'ajaxComplete', 'attributes': { 'type': opts.type || '', 'url': fullUrl.href || '', 'queryParameters': queryParameters, 'pathname': pathname || '', 'hostname': fullUrl.hostname || '', 'protocol': fullUrl.protocol || '', 'fragment': fullUrl.hash || '', 'statusCode': jqXhr.status || '', 'statusText': jqXhr.statusText || '', 'headers': headers, 'timestamp': evt.timeStamp || '', 'contentType': opts.contentType || '', 'response': (jqXhr.responseJSON || jqXhr.responseXML || jqXhr.responseText || '') } }); }); } function objMap(data, delim, spl, decode) { var obj = {}; if (!data || !delim || !spl) return {}; var arr = data.split(delim); if (arr) { for (var i = 0; i < arr.length; i++) { var item = decode ? decodeURIComponent(arr[i]) : arr[i]; var pair = item.split(spl); var key = trim_(pair[0]); var value = trim_(pair[1]); if (key && value) { obj[key] = value; } } } return obj; } function trim_(str) { if (str) { return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } } })(); </script>
2. Add Trigger to Fire Listener
Trigger Type: All Pages
This ensures the listener is active across your entire website.
3. Create Data Layer Variables (Important Step)
Go to Variables → New → Data Layer Variable
Create:
- Variable Name: ajaxURL
Data Layer Variable Name: attributes.url - Variable Name: ajaxStatus
Data Layer Variable Name: attributes.statusCode
Advanced (Recommended) – Confirmation Variable
Since this listener stores response inside attributes.response, you can extract deeper insights.
If your form response contains confirmation like:
“data”: {
“confirmation”: “success”
}
}
Then create:
- Variable Name: confirmationStatus
- Data Layer Variable Name: attributes.response.data.confirmation
Why This Setup Is Powerful
- attributes.statusCode = 200 → Request success
- confirmationStatus = success → Actual form success
This avoids false tracking.
4. Create Custom Event Trigger
Go to Triggers → New
- Trigger Type: Custom Event
- Event Name: ajaxComplete
Add conditions:
- ajaxURL contains contact
- AND
- confirmationStatus equals success
5. Create GA4 Event Tag
- Tag Type: GA4 Event
- Event Name: form_submit_ajax
Add parameters:
- form_url → {{ajaxURL}}
- status → {{ajaxStatus}}
- confirmation → {{confirmationStatus}}
Attach your trigger.
6. Test Using Preview Mode
- Open GTM Preview
- Submit form
Check:
- ajaxComplete event fires
- Tag triggers correctly
7. Publish Your Container
Once confirmed:
Click Submit → Publish
How to Confirm in Google Analytics (GA4)
Once your setup is complete, it’s important to verify that everything is working correctly.
Go to your GA4 property:
- Open Realtime Report
- Submit your form again on your website
- Look for the event:
form_submit_ajax
If you can see the event in real-time, your tracking is working perfectly.
You can also mark this event as a conversion inside GA4 to start measuring performance in your reports.
Important Notes
- This listener works only if your site uses jQuery AJAX
- For modern sites using fetch API, a different listener is needed
- Always filter using ajaxURL to avoid noise from other AJAX calls
Pro Tip (Best Practice)
If possible, use developer-based tracking:
event: ‘formSubmissionSuccess’
});
Cleaner, faster, and 100% accurate.
Final Thoughts
Using this advanced AJAX listener gives you deep visibility into form submissions, especially on dynamic websites.
With the right setup, you can:
- Track real conversions (not just clicks)
- Avoid false positives
- Improve ad performance
- Make smarter marketing decisions
Powered by Skyno Digital
At Skyno Digital, we don’t just set up tracking—we build data-driven systems that scale businesses. From GTM implementation to advanced conversion tracking, every setup is designed to give you clarity, control, and growth.
Because in today’s digital world, the brands that track better… grow faster.



