Many times, a prospect turns into a lead by completing a form on your website. To measure the users who are converting and the actions they are taking on your forms, you need to track form submissions. The problem is that there is no global standard for the development of forms, which means that there are different ways to track each type of lead submitted. In other words, to set up event tracking in Google Analytics, you may need to explore several options to figure out what will work best for you.

How to Implement Google Tag Manager for Form Submit Tracking

  1. Built-In Form Trigger

The first option is to use the built-in form trigger. Although this method does have a poor success rate, it’s worth trying just because the Google Tag Manager implementation is simple and the data it collects is more comprehensive than with any other conversion tracking tag method. All you need to do is configure the trigger to listen for submit forms and send a dataLayer event to gtm.formSubmit.

By default, the built-in form listener is disabled, as are all other form variables. You can find them by heading to “Built-In Variables” and choosing “Configure.” Before you go any further, ensure that the Google Tag Manager form submit variables “Form Element,” “Form Classes,” “Form ID,” “Form Target,” “Form URL,” and “Form Text” are all checked.

Next, create a trigger with the title “All form submissions” and the trigger type “Form submission.” Leave the “Wait for tags” unchecked but do click the box next to “Check validation.” For “Fire on,” set conditions according to where the form is on your website.

You can see if the auto-event listener is working by clicking “Preview” in preview and debug mode. An orange banner should appear. If it does not, check the setup of your preview and debug mode. If it does appear, head to the page of the site where you have the form.

Fill in all the fields of the form and submit. If the auto-event listener works with your form, a gtm.formSubmit event will fire. In the case that happens, continue testing by submitting the form again, this time leaving one of the required fields blank. If the gtm.formSubmit appears again, the auto-event listener is unsuitable for your form.

If you’ve established that the auto-event listener works, it’s time to create a trigger for your form. Do this by clicking the gtm.formSubmit event and choosing “Variables.” Look for a form variable that is unique to the form — it is best to find a Form ID variable.

Next, head to “Triggers” and choose “New.” Under configuration, set the trigger type as “Form submission.” Then, click “Check validation” and input “Page URL matches RegEX (.*)” Finally, set the trigger to fire on some forms with the rule “Form ID equals [Form ID]” — or whatever variable you are using.

  1. Fixing the Form Trigger

For Google Tag Manager form tracking to work, the form trigger must dispatch a valid form submit event. This is only possible when two things happen. First, the form needs to dispatch a valid submit browser event. Second, nothing must prevent the event from propagating to the document node. Many forms violate either one or both of these conditions.

There are several ways to correct the problem. The most robust is to simply implement a standard submit browser event to propagate to the document node.

Another option is to implement a custom dataLayer.push() into the callback function. This needs to be invoked upon successful submission to push the formSubmissionSuccess into the dataLayer as a value of the event key. After this, you need to create a custom event trigger to wait for an event called formSubmissionSuccess.

  1. Thank-You Page Tracking

One last option to fix your form listener may work if your form redirects users to a thank-you page. This is only suitable if users arrive at a unique webpage; users must not be able to navigate to the thank-you page without first submitting the form.

The method involves creating a pageview trigger configured to “Some page views.” The rule should be either “Page Path equals” or “Page URL contains” followed by the address. Name your trigger something like “Pageview – Successful Form Submission.”

  1. Writing a Form Auto-Event Listener

An alternative to using the built-in auto-listener is to write your own. This will work only if the form is not custom made — for example, if your website runs on WordPress.

To start, check if there is already a Google Tag Manager listener for the plugin you have. Furthermore, confirm that it works correctly. A common error in listeners is dispatching form submission events when a required field is empty.

Next, check if there is JavaScript API for the type of form. You’re looking for an API method related to successful submissions. If you do find something, ensure that it is ready to use for your skill level.

If you are lucky with the above, copy the code snippet and merge it with a window.dataLayer push event. Then, create a custom HTML tag using the full code. Save the tag, assign a trigger, and test the result.

  1. AJAX Form Tracking

It is possible that your form may be built on AJAX, in which case you need an AJAX listener. For this, all you need to do is copy the following code and paste it in a Custom HTML tag set to fire on all pages:

<script id="gtm-jq-ajax-listen" type="text/javascript">
(function() {

'use strict';
var $;
var n = 0;

function init(n) {

// Ensure jQuery is available before anything
if (typeof jQuery !== 'undefined') {

// Define our $ shortcut locally
$ = jQuery;

// Check for up to 10 seconds
} else if (n < 20) {

setTimeout(init, 500);



function bindToAjax() {

$(document).bind('ajaxComplete', function(evt, jqXhr, opts) {

// Create a fake a element for magically simple URL parsing
var fullUrl = document.createElement('a');
fullUrl.href = opts.url;

// IE9+ strips the leading slash from a.pathname because who wants to get home on time Friday anyways
var pathname = fullUrl.pathname[0] === '/' ? fullUrl.pathname : '/' + fullUrl.pathname;
// Manually remove the leading question mark, if there is one
var queryString =[0] === '?' ? :;
// Turn our params and headers into objects for easier reference
var queryParameters = objMap(queryString, '&', '=', true);
var headers = objMap(jqXhr.getAllResponseHeaders(), '\n', ':');

// Blindly push to the dataLayer because this fires within GTM
'event': 'ajaxComplete',
'attributes': {
// Return empty strings to prevent accidental inheritance of old data
'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 || '',
// Defer to jQuery's handling of the response
'response': (jqXhr.responseJSON || jqXhr.responseXML || jqXhr.responseText || '')
function objMap(data, delim, spl, decode) {
var obj = {};
// If one of our parameters is missing, return an empty object
if (!data || !delim || !spl) {

return {};


var arr = data.split(delim);
var i;

if (arr) {

for (i = 0; i < arr.length; i++) {

// If the decode flag is present, URL decode the set
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;


// Basic .trim() polyfill
function trim_(str) {
if (str) {

return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');



* v0.1.0
* Created by the Google Analytics consultants at
* Written by @notdanwilkerson
* Documentation:
* Licensed under the Creative Commons 4.0 Attribution Public License

Use the previously described method to check that this works in preview and debug mode. If it does work, an ajaxComplete event will appear. Click the event and choose “Data Layer.” In the data that appears, find “response” and look for “The message has been successfully sent.” Use this to create a trigger by heading to “Variables,” finding “User-Defined,” and clicking “New.”

Under “Variable configuration,” select “Data Layer Variable.” Give it the name “attributes.response” and use something like “dlv – attributes.response” for the variable title. Leave everything else as the default. When you test the tracking, it should bring up “The message has been successfully sent” and not “undefined.”

Once you’re set up correctly, create a custom event trigger with the name “ajaxComplete.” Configure it to fire on some custom events with the condition “dlv – attributes.response contains The message has been successfully sent.”

  1. Field Value

An alternative to manage Google Tag Manager conversion tracking is to capture field value. A JavaScript variable will capture the value the user inputs into the form field. It accesses the value attribute of the form field with inputFieldId.

function() {
 var field = {{Form Element}}.querySelector('#inputFieldId');
 return field ? field.value : undefined;

Note that you can only use this form submit in Google Tag Manager to track fields. You’ll need a different approach for checkboxes, radio buttons, and drop-down lists. It is also important that you ensure that this method does not gather identifiable user information.

  1. Tracking with Element Visibility Trigger

By using an element visibility trigger, you can track when an event appears on the screen, such as when a message appears after the successful submission of a form. To do this, you need to locate the aspect of the message you want Google Tag Manager to track. Select this part of the message, right click on it, and choose “Inspect Element.” This will pull up your browser’s developer tools. Look for a parameter called ID or, failing that, CSS class.

In your Google Tag Manager, head to “Triggers” and create a new element visibility trigger. Choose the selection method according to whether you have ID or CSS class and paste the corresponding information into the field. Bear in mind that every class is preceded by a dot in CSS — for instance “.thanks” rather than “thanks.” Lastly, check the box next to “Observe DOM changes.”

  1. Radio Button Values and Checkbox Values

If you have a single radio button value, you can use the checked radio button method. This will return the value of the checked radio button using radioName. If it finds no checked button, it will return “undefined.”

function() {
 var radioName = "radioName";
 var checked = {{Form
Element}}.querySelector('[name="' + radioName +
 return checked ? checked.value : undefined;

You can use the same method to collect checkbox value, again using the checked property to determine if the element is checked. Like with the radio button, this only works for a single checkbox.

  1. Drop-Down List Values

In the case that you want to run Google conversion tracking for a form with a drop-down list, you’ll need to access the option selected in the list and capture its value. You may expect that you could simply check for the value of the list, but this won’t work. Rather, you need to retrieve the drop-down list with selectListId and the script will return the value of the selected option. If this list does not exist, the script will return “undefined.”

function() {
 var selectList = {{Form
 return selectList ?
selectList.options[selectList.selectedIndex].value :

  1. Multiple Values

The above Google Analytics event tracking form submit options are unhelpful if you have multiple values in your form, such as when it is possible for users to check more than one box in a single group. In these cases, you can either return a concatenated string of checked item values or return selected values in a multiple selection list.

If you choose the first option, you’ll receive a string within a group called groupName:

function() {
 var groupName = "groupName";
 var elems = {{Form
Element}}.querySelectorAll('[name="' + groupName +
 var vals = [];
 var i, len;
 for (i = 0, len = elems.length; i < len; i++) {
 return vals.length ? vals.join(' ') : undefined;

You have the option to return just the plain array to continue processing it in the tag that calls the variable by changing the return statement to:

 return vals.length ? vals : undefined;

With the second option, you will receive a concatenated string of all the selected options where selectListId is the ID:

function() {
 var selectList = {{Form
 var options = selectList ? selectList.options : [];
 var vals = [];
 var i, len;
 for (i = 0, len = options.length; i < len; i++) {
   if (options[i].selected) {
 return vals.length ? vals.join(' ') : undefined;

  1. Data Layer Event

For the data layer event method, you need to ask a developer to implement a custom dataLayer.push() into the callback function. A dataLayer variable such as “form Type” is a good choice for more than one form. You may also like to include a parameter for form position. Include both in Google Tag Manager by creating data layer variables. Finally, you need a custom event trigger to fire on all custom events.

  1. DOM Scraping

The very last option to try is DOM scraping. This method is problematic if you often update your website code, but it can work in situations such as when you lack a unique URL on your thank-you page.

Create a DOM variable that looks for a success message by right clicking the message and selecting “Inspect.” Search for a unique ID or CSS class. Copy what you find and use it to create a DOM element. Head to “Variables” and choose “New” followed by “DOM Element Variable.” Pick the appropriate selection method and paste the element selector you copied earlier. Give no attribute name but set the variable title like “DOM – Form Success Message.”

Finally, create a trigger for the DOM variable. This should have the type “pageview” and should fire on some pages. Set the rule “DOM – Form Success Message equals [success message].”

As you can see, you have a variety of options from Google Analytics for submit tracking. To start out, try the built-in form trigger. If that fails, move on to other Google Analytics tag tracking methods. Just make sure that you enable all the necessary form submit Google Tag Manager variables before you dive in — or else these options will fail as well.