Tracking HubSpot V4 Forms & Multi-Step Forms in GTM
This post covers tracking forms created in the new HubSpot form editor that use API Version 4. It also covers multi-step form navigation events and extracting field values (such as email) to the data layer.
I wasn’t able to find a guide for tracking new HubSpot v4 forms — so here we go.
If you are using legacy forms, you can use a guide like this one by Jason Nettles.
The HubSpot API documentation for global form events outlines the form events we can listen to. Translating these into something GTM-compatible was a bit tricky, but hopefully this acts as a quick start.
Events Available
Listeners included in the code below:
- Form Ready —
hs-form-event:on-ready - Successful Form Submission —
hs-form-event:on-submission:success - Failed Form Submission —
hs-form-event:on-submission:failed - Form Navigate —
hs-form-event:on-interaction:navigate
You can easily extend this to add:
- Form Navigate Next —
hs-form-event:on-interaction:navigate:next - Form Navigate Previous —
hs-form-event:on-interaction:navigate:previous
Steps for Implementation
- Create a custom HTML Tag in GTM and add your desired listeners (code below). Set it to trigger on all pages.
- Set up data layer variables to capture values pushed via the events. For instance,
user-emailis pushed on a successful form submission. - Set up triggers for the custom events you want to track (e.g.
hubspot-form-success-v4). - Set up analytics tags and user-provided data variables (e.g. for enhanced conversions) as required.
Listener Code
Form Ready
<script> window.addEventListener('hs-form-event:on-ready', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId;
window.dataLayer.push({ 'event': 'hubspot-form-ready-v4' }); });</script>Successful Form Submission
Note the method for extracting the email field value. This is passed to the GTM data layer and can be used for enhanced conversions.
<script> window.addEventListener('hs-form-event:on-submission:success', function (event) { var form = HubSpotFormsV4.getFormFromEvent(event); // form.getFieldValue allows us to get values of specific fields // based on their field name form.getFieldValue('0-1/email').then(function (email) { var emailValue = email || '';
// Push all values in one atomic call window.dataLayer.push({ 'user-email': emailValue, 'event': 'hubspot-form-success-v4' }); }); });</script>Failed Form Submission
<script> window.addEventListener('hs-form-event:on-submission:failed', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId;
window.dataLayer.push({ 'event': 'hubspot-form-failed-v4' }); });</script>Form Navigate (Multi-Step Forms)
<script> window.addEventListener('hs-form-event:on-interaction:navigate', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId; var currentStep = event.detail.currentStep; var form = HubSpotFormsV4.getFormFromEvent(event);
window.dataLayer.push({ 'event': 'hubspot-form-navigate-v4', }); });</script>Full Boilerplate Code
Put this in a single custom HTML tag in GTM, triggered on all pages:
<script> window.addEventListener('hs-form-event:on-ready', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId;
window.dataLayer.push({ 'event': 'hubspot-form-ready-v4' }); });
window.addEventListener('hs-form-event:on-submission:success', function (event) { var form = HubSpotFormsV4.getFormFromEvent(event); form.getFieldValue('0-1/email').then(function (email) { var emailValue = email || '';
window.dataLayer.push({ 'user-email': emailValue, 'event': 'hubspot-form-success-v4' }); }); });
window.addEventListener('hs-form-event:on-submission:failed', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId;
window.dataLayer.push({ 'event': 'hubspot-form-failed-v4' }); });
window.addEventListener('hs-form-event:on-interaction:navigate', function (event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId; var currentStep = event.detail.currentStep; var form = HubSpotFormsV4.getFormFromEvent(event);
window.dataLayer.push({ 'event': 'hubspot-form-navigate-v4', }); });</script>Extended Example with Custom Logic
This is closer to a real-world implementation — it pulls a custom form_type field value and uses it to fire more specific GTM triggers:
<script> // Handle the 'on-ready' event (optional). window.addEventListener('hs-form-event:on-ready', function(event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId; // console.log('Form Ready:', formId); });
// Handle successful form submission window.addEventListener('hs-form-event:on-submission:success', function(event) { var form = HubSpotFormsV4.getFormFromEvent(event);
form.getFieldValue('0-1/email').then(function(email) { var emailValue = email || ''; if (!emailValue) { console.log('Email field is empty!'); }
// Nest the second getFieldValue call inside the first form.getFieldValue('0-1/form_type').then(function(formType) { var formTypeValue = formType || ''; if (!formTypeValue) { console.log('Form Type field is empty!'); }
window.dataLayer.push({ 'user-email': emailValue, 'form-type': formTypeValue, // custom form property 'event': 'hubspot-form-success-v4' }); }); }); });
// Handle failed form submissions window.addEventListener('hs-form-event:on-submission:failed', function(event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId; console.log('Form Submission Failed:', formId); });
// Handle multi-step form navigation window.addEventListener('hs-form-event:on-interaction:navigate', function(event) { var formId = event.detail.formId; var instanceId = event.detail.instanceId; var currentStep = event.detail.currentStep; var form = HubSpotFormsV4.getFormFromEvent(event);
form.getFieldValue("0-1/form_type") .then(function (value) { if (!value) { window.dataLayer.push({ 'event': 'hubspot-form-nav-v4' }); } else { window.dataLayer.push({ 'event': 'hubspot-form-nav-v4', 'form-type': value }); } }); });</script>Remember to test thoroughly and let me know if you run into any issues!
Need help with this?
We do this work every day. If you're dealing with any of the issues covered in this post, we can help.