Read this carefully…
Forms are the biggest lead generators on the web, but they can be very difficult to track, especially if you’ve done very little HTML and CSS.
The complexity lies in tracking ONLY valid form submissions.
You may be interested in more specific guides:
In this article, I explain 5 different methods.
Before adopting a particular method, remember to check its conditions of use by carrying out the suggested diagnostic.
Each method is associated with a level of difficulty (Easy, Medium or Difficult).
Start with the conditions of use of method 1, then let us guide you until you find the method that works for you.
Method 1: The “Form Submission” trigger
Method 1: Conditions of use
To check each condition, take a look at Method 1: Run the diagnosis.
flowchart TD
A[Does the form sends the <b>'submit'</b> event ?]
B[Is the event <b>gtm.formSubmit</b> triggered <b>ONLY</b>\n after a valid form submission?]
C[Do you have access to variables \n to determine whether the form is valid or not?]
D[<a href="#method-1-use-it">Use method 1</a>]
E[<a href="#method-2-url-or-confirmation-page">Check method 2</a>]
A -- YES --> B
A -- NO --> E
B -- NO --> C
B -- YES --> D
C -- YES --> D
C -- NO --> E
Method 1: Run the diagnosis
Looking at your form’s HTML code, if you notice that the <form>
tag contains the action
attribute as in the example below, chances are your form is sending a submit
event.
<form method="POST" action="https://some-url.com">
<!-- ... -->
</form>
Similarly, if you notice that the HTML code of the form submit button is type="submit"
, your form should send a submit
event.
<form>
<input type="submit" value="Send"/>
<!-- OR -->
<button>Send</button> <!-- The <button> tag sends a submit event by default -->
<!-- OR -->
<button type="submit">Send</button>
</form>
Another way to find out is to monitor the gtm.formSubmit
event in Google Tag Manager’s Debug Mode.
To do this, you’ll need to add the “Form Submission” trigger like this:
![Form Submission trigger configuration in Google Tag Manager](https://data-marketing-school.com/images/blog/google-tag-manager/forms/form_trigger.en_hucf153a22c0e3ec2a05047dd7d6d201c4_25962_918x0_resize_q75_h2_box.webp)
If the form sends a submit
event, you should see this in Google Tag Manger’s Debug Mode:
![]() | ![]() |
gtm.formSubmit linked to the Form Submission trigger |
If you’ve mastered a little Javascript, to be totally sure that your form is sending a submit event, I invite you to install this code on your form page and see if an alert appears on the screen.
document.addEventListener("DOMContentLoaded", function() {
document.querySelector("form").addEventListener("submit", function() {
alert("Your form sends the submit event !!");
});
});
This is what you should get if your form sends a submit event:
![Alert proving that your form sends a submit event](https://data-marketing-school.com/images/blog/google-tag-manager/forms/alert_submit.en_hu4cc9281999fd886af71a3d8a231381df_5550_448x0_resize_q75_h2_box.webp)
submit
eventAt this stage, you need to determine whether the form sends a submit event only when it is valid or each time it is submitted.
If you have access to variables that allow you to determine the validity of the form, you can check the Check Validation
checkbox in the trigger.
![Using a variable to check form validation](https://data-marketing-school.com/images/blog/google-tag-manager/forms/variable_validation_form.en_hu3478473b63371db20cd85aca97d74e7c_54047_911x0_resize_q75_h2_box.webp)
If method 1 doesn’t work for your form, please proceed to method 2.
Method 1: Use it
As seen during diagnostic, you need to use the Form Submission trigger.
![Form Submission trigger configuration in Google Tag Manager](https://data-marketing-school.com/images/blog/google-tag-manager/forms/form_trigger.en_hucf153a22c0e3ec2a05047dd7d6d201c4_25962_918x0_resize_q75_h2_box.webp)
Check the Check Validation
checkbox only if you have a variable that allows you to determine whether the form submission is valid.
Configure GA4 event
We’ll now configure GA4’s generate_lead
event and link it to this trigger.
![GA4 event tag configuration](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_form_trigger.en_huc53bf4f3d9b39f9bd61538eb73c0cfeb_40056_894x0_resize_q75_h2_box.webp)
Method 1: Test it
We then test the configuration using Google Tag Manager’s debug mode.
![GA4 event tag testing](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_form_submit_hu9c39c29dff3b4e92af5d8ac012399e79_48732_622x0_resize_q75_h2_box.webp)
We then check that the generate_lead
event has been correctly sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
generate_lead event debuggingMethod 2: URL or Confirmation page
Method 2: Conditions of use
To check each condition, take a look at Method 2: Run the diagnosis.
flowchart TB
A[Does the page reload \n after a form submission?]
B[Does the URL or the page change <b>ONLY</b> \n after a valid form submission?]
C[If the page does change, is the \n confirmation page unique?]
D[After a form submission, does the <i>document.referrer</i> variable \n on the confirmation page \n indicates the URL of the form page?]
E[Do you have access to variables \n to determine a valid form submission?]
F[<a href="#method-2-use-it">Use method 2</a>]
G[<a href="#method-3-ajax-listener">Check method 3</a>]
H[<a href="#method-4-element-visibility-trigger">Check method 4</a>]
I[Ask a developer to add a unique \n confirmation page for each form]
A -- YES --> B
A -- NO --> G
B -- YES --> C
B -- NO --> E
C -- YES --> F
C -- NO --> D
D -- YES --> F
D -- NO --> I
E -- YES --> C
E -- NO --> H
Method 2: Run the diagnosis
When you submit a valid form, the URL fragment may change (https://example.com/#form-sent
) or you may be redirected to a confirmation page (https://example.com/confirmation-page
).
On the confirmation page, you need to identify whether it’s unique by asking the developer or webmaster. And if it’s used for other forms or pages on the website, you may be able to differentiate it with the document.referrer
variable.
This variable contains the previous URL that was visited. If it’s indeed the form page you want to track, then you can use method 2.
To determine this, submit the form, then right-click > Inspect on the confirmation page.
Then go to the Console tab, type document.referrer
and press enter.
![Display previous URL from the confirmation page](https://data-marketing-school.com/images/blog/google-tag-manager/forms/document_referrer_console_hu42671877c46862eaddf39e0afea98327_21326_525x0_resize_q75_h2_box.webp)
If the URL displayed is indeed that of your form page, you can continue with method 2.
Method 2: Use it
The URL fragment changes
Let’s say that when you submit a valid form, the #form-sent
fragment is added to the URL.
We’re going to trigger our GA4 generate_lead
event only when this fragment is in the URL.
To do this, we’ll first create a Fragment variable:
![Variable to get the fragment in the URL](https://data-marketing-school.com/images/blog/google-tag-manager/forms/variable_url_fragment.en_hud5b3ce89433042835e116b6bfbd4e583_24362_902x0_resize_q75_h2_box.webp)
Then we create a trigger on page views that contain the #form-sent
fragment:
![Page view trigger containing the fragment](https://data-marketing-school.com/images/blog/google-tag-manager/forms/trigger_page_view_fragment.en_hueead5335d2d22711569f3403994cd040_39957_1025x0_resize_q75_h2_box.webp)
We then link the GA4 event tag to this trigger:
![GA4 event tag linked to the Page View trigger](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_url_fragment_trigger.en_hufffecb7dce1cf266d26b6d3c1d54b73b_41724_900x0_resize_q75_h2_box.webp)
Confirmation page
When you submit a valid form, you’re redirected to a single confirmation page.
We’ll create a Page View trigger with a condition on the page URL.
![Page View trigger on the confirmation page](https://data-marketing-school.com/images/blog/google-tag-manager/forms/trigger_page_view_confirmation.en_hu3b1f0733db08fa0ce81766a5df8facb4_31391_901x0_resize_q75_h2_box.webp)
We then link the GA4 event tag to this trigger.
![GA4 event tag linked to the confirmation page trigger](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_page_confirmation_trigger.en_hu476df3f7c52df5531c978402aad8705b_41630_893x0_resize_q75_h2_box.webp)
Method 2: Test it
The URL fragment changes
We then test the configuration in Google Tag Manager’s Debug Mode:
![GA4 event debugging in GTM's Debug Mode](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_url_fragment.en_huf01e8c54425c71c19706ca0fc7adeb52_49132_618x0_resize_q75_h2_box.webp)
We then check that the generate_lead
event has been correctly sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
generate_lead event debuggingConfirmation page
We then test the configuration in Google Tag Manager’s Debug Mode:
![GA4 event debugging in GTM's Debug Mode](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_page_confirmation.en_hub2fbf48f94e1f233f45d32c1be6e9374_76642_786x0_resize_q75_h2_box.webp)
We then check that the generate_lead
event has been correctly sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
generate_lead event debuggingMethod 3: AJAX Listener
Method 3: Conditions of use
To check each condition, take a look at Method 3: Run the diagnosis.
flowchart TB
A[Does the form submission send the <b>ajax_complete</b> \nevent after the installation of the code below?]
B[Does variables in the Data Layer allow you to\n know whether the form submission is vqlid or not?]
C[<a href="#method-3-use-it">Use method 3</a>]
D[<a href="#method-4-element-visibility-trigger">Check method 4</a>]
A -- YES --> B
B -- YES --> C
A -- NO --> D
B -- NO --> D
Method 3: Run the diagnosis
First, you need to install the code below between the <head>
tags on your form page.
You can ask a developer to set it up, or do it yourself via Google Tag Manager.
<script>
window.dataLayer = window.dataLayer || [];
var origin_open = XMLHttpRequest.prototype.open;
var origin_send = XMLHttpRequest.prototype.send;
var xhrRequestBody = "";
XMLHttpRequest.prototype.send = function(body) {
if(body instanceof FormData) {
xhrRequestBody = Object.fromEntries(body);
} else if(typeof body == "string" || body instanceof String) {
body = decodeURIComponent(body);
var a = body.split("&");
var b = [];
a.forEach(function(el) { return b.push(el.split("=")) });
xhrRequestBody = Object.fromEntries(b);
}
origin_send.apply(this, arguments);
};
XMLHttpRequest.prototype.open = function() {
this.addEventListener('loadend', function() {
dataLayer.push({
event: "ajax_complete",
requestType: "XHR",
requestBody: xhrRequestBody || "",
location: document.location.href || "",
path: document.location.pathname || "",
fragment: document.location.hash || "",
protocol: document.location.protocol || "",
hostname: document.location.hostname || "",
statusCode: this.status || "",
statusText: this.statusText || "",
responseType: this.responseType || "",
readyState: this.readyState || ""
});
});
origin_open.apply(this, arguments);
};
window.fetch = new Proxy(window.fetch, {
apply: function (target, that, args) {
var temp = target.apply(that, args);
temp.then(function(res) {
dataLayer.push({
event: "ajax_complete",
requestType: "Fetch",
location: document.location.href || "",
path: document.location.pathname || "",
fragment: document.location.hash || "",
protocol: document.location.protocol || "",
hostname: document.location.hostname || "",
statusCode: res.status || "",
statusText: res.statusText || "",
requestedURL: res.url || ""
});
});
return temp;
},
});
/*
* v0.1.0
* Created by Data Marketing School at http://www.data-marketing-school.com
* Written by https://www.linkedin.com/in/lucasrollin/
*/
</script>
To do this via Google Tag Manager, create a custom HTML tag, then insert the above code.
![Create an AJAX Listener in a custom HTML tag within GTM](https://data-marketing-school.com/images/blog/google-tag-manager/forms/gtm_ajax_listener.en_huc7b61401a295ce6b4c3894163246dff8_212744_1376x0_resize_q75_h2_box.webp)
Then check in Google Tag Manager for the ajax_complete
event displayed when you submit your form.
The variables transmitted with the event should also enable you to differentiate between valid and invalid form submissions.
If this is the case, you can use method 3. If not, go to method 4.
Method 3: Use it
To trigger a tag following the ajax_complete
event, we’ll create a custom event trigger and also create the statusText
and statusCode
variables in Google Tag Manager.
Create statusText and statusCode variables
These variables will be used in the next section when we configure the trigger.
![]() | ![]() |
statusCode and statusText |
Create the ajax_complete custom event trigger
We now create the Custom event trigger on the ajax_complete
event.
![Trigger on ajax_complete event when statusCode is 200](https://data-marketing-school.com/images/blog/google-tag-manager/forms/dle_ajax_complete_trigger.en_huc7b61401a295ce6b4c3894163246dff8_106065_1366x0_resize_q75_h2_box.webp)
ajax_complete
event when statusCode is 200In this example, I’m checking that the statusCode
is equal to 200. This check will depend on the statusCode
sent by your form on a valid submission.
We then associate the GA4 event tag with this trigger:
![GA4 event tag linked to the ajax_complete trigger](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_dle_trigger.en_huc7b61401a295ce6b4c3894163246dff8_84955_1238x0_resize_q75_h2_box.webp)
ajax_complete
triggerMethod 3: Test it
We then test the configuration with Google Tag Manager’s debug mode for both valid and invalid form submission.
![GA4 event trigger debugging on a valid AJAX form submission](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_ajax_complete_200.en_huce8a8125372ac7b0a0629c2b84cbd960_179750_1686x0_resize_q75_h2_box.webp)
![GA4 event trigger debugging on an invalid AJAX form submission](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_ajax_complete_401.en_huce8a8125372ac7b0a0629c2b84cbd960_172253_1660x0_resize_q75_h2_box.webp)
Next, we check that the generate_lead
event has been sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
event debuggingMethod 4: “Element Visibility” trigger
Method 4: Conditions of use
To check each condition, take a look at Method 4: Run the diagnosis.
flowchart TD
A[Does the form submission \n displays a confirmation message?]
B[Is the confirmation message already \n in the HTML code when the page loads?]
C[<a href="#method-4-use-it">Use method 4</a>]
D[<a href="#method-5-the-datalayer">Check method 5</a>]
A -- YES --> B
A -- NO --> D
B -- YES --> C
B -- NO --> D
Method 4: Run the diagnosis
Your form displays a confirmation message for valid submissions like this:
![Conformation message when the form is submitted](https://data-marketing-school.com/images/blog/google-tag-manager/forms/valid_form_message.en_huc7b61401a295ce6b4c3894163246dff8_33237_830x0_resize_q75_h2_box.webp)
And this message is already in the HTML code when the page loads, but simply hidden by the CSS, so you can use method 4.
<p id="valid" class="rounded-sm hidden text-slate-100 bg-green-700 px-3 py-3">The form has been sent.</p>
Here, the message contains the hidden
class. It is hidden by default when the page is loaded and is displayed on a valid form submission.
Method 4: Use it
We’ll create an Element visibility trigger to detect when the <p>
tag containing the message “The form has been sent.” becomes visible.
To do this, we specify the tag id (valid
) and check the “Observe DOM changes” checkbox.
![Configuration of Element Visibility trigger](https://data-marketing-school.com/images/blog/google-tag-manager/forms/gtm_element_visible_trigger.en_huce8a8125372ac7b0a0629c2b84cbd960_155261_1280x0_resize_q75_h2_box.webp)
By default, the Element visibility trigger is triggered when the page is scrolled.
In the case of a form submission, there is no scroll, so it is necessary to check the “Observe DOM changes” checkbox.
We then link the GA4 event tag with this trigger:
![GA4 event tag linked with the Element Visibility trigger](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_element_visible_trigger.en_huc7b61401a295ce6b4c3894163246dff8_108429_1308x0_resize_q75_h2_box.webp)
Method 4: Test it
We then test the configuration with Google Tag Manager’s debug mode for both valid and invalid form submission.
![Debug GA4 event tag trigger when the confirmation message appears](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_element_visible.en_huc7b61401a295ce6b4c3894163246dff8_188256_1680x0_resize_q75_h2_box.webp)
Next, we check that the generate_lead
event has been sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
event debuggingMethod 5: The dataLayer
Method 5: Conditions of use
flowchart TD
A[Can you get help from a developer?]
B[<a href="#method-5-use-it">Use method 5</a>]
C[Rest in Peace]
A -- YES --> B
A -- NO --> C
Method 5: Use it
Ask a developer to trigger this code when a valid form submission happens:
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: "form_sent"
});
You can modify the event name and add additional parameters if you wish to collect more information on the form.
Once your developer has implemented the code, all that’s left to do is set up a custom event trigger on the form_sent
event or whatever event you want to use.
![Custom event trigger on form_sent data layer event](https://data-marketing-school.com/images/blog/google-tag-manager/forms/dle_form_sent_trigger.en_huc7b61401a295ce6b4c3894163246dff8_71360_1106x0_resize_q75_h2_box.webp)
form_sent
data layer eventThen, we link it to the GA4 event tag:
![Balise GA4 associée au déclencheur sur l'événement form_sent](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_dle_form_sent.en_huc7b61401a295ce6b4c3894163246dff8_113632_1420x0_resize_q75_h2_box.webp)
form_sent
Method 5: Test it
We then test the configuration with Google Tag Manager’s debug mode for both valid and invalid form submission.
![Debug of GA4 event tag on form_sent event](https://data-marketing-school.com/images/blog/google-tag-manager/forms/debug_form_sent_event.en_hudfc13013fdd94d2d2f8efd34ce9bbc3b_61375_767x0_resize_q75_h2_box.webp)
form_sent
eventNext, we check that the generate_lead
event has been sent to GA4 using the Debug View.
![GA4 generate_lead event debugging](https://data-marketing-school.com/images/blog/google-tag-manager/forms/ga4_generate_lead_debug_view_hu9bf273f1bc37b012f2f011d92ddeeacb_22516_577x0_resize_q75_h2_box.webp)
generate_lead
event debuggingDidn't find what you were looking for?
Get help from the Data Marketing Club