Track Hubspot Forms with Google Tag Manager

Updated: Friday, June 6, 2025

Hubspot forms

Forms created with Hubspot are difficult to track with Google Tag Manager’s native triggers.

I’ll show you how to track only valid submissions of a Hubspot form, and also how to get user data (such as email address) to send to advertising platforms.

Install Hubspot Form listener

To listen for valid Hubspot form submissions, you need to add this code to Google Tag Manager with a custom HTML tag.

Hubspot Form Listener
  1<script>
  2if(typeof HubspotFormsV4 != 'undefined') {
  3  window.addEventListener("hs-form-event:on-submission:success", function(event) {
  4    var form = HubspotFormsV4.getFormFromEvent(event);
  5    if (form) {
  6      window.dataLayer = window.dataLayer || [];
  7      form.getFormFieldValues().then(function(fieldValues){
  8        window.dataLayer.push({
  9          event: "generate_lead",
 10          form_id: form.getFormId(),
 11          instance_id: form.getInstanceId(),
 12          conversion_id: form.getConversionId(),
 13          user_data: normalizeUserData(fieldValues, 'v4'),
 14          other_properties: getOtherProperties(fieldValues)
 15        });
 16      });
 17    }
 18  });
 19}
 20
 21window.addEventListener('message', function(event) {
 22  if (event.data && event.data.type == "hsFormCallback" &&
 23      (event.data.eventName == "onFormSubmitted")) {
 24    window.dataLayer = window.dataLayer || [];
 25    window.dataLayer.push({
 26      event: 'generate_lead',
 27      form_id: event.data.id,
 28      conversion_id: event.data.data.conversionId,
 29      user_data: normalizeUserData(event.data.data.submissionValues, 'v3'),
 30      other_properties: getOtherProperties(event.data.data.submissionValues, 'v3')
 31    });
 32
 33  }
 34});
 35
 36function getOtherProperties(fieldValues, version) {
 37
 38  var other_properties = {};
 39
 40  var user_data_to_exclude = [
 41    'email',
 42    'phone',
 43    'mobilephone',
 44    'firstname',
 45    'lastname',
 46    'address',
 47    'city',
 48    'state',
 49    'country',
 50    'zip'
 51  ];
 52
 53  if(version == 'v4') {
 54    fieldValues.forEach(function(field) {
 55      var name = field.name;
 56      var value = field.value;
 57      var cleanName = name.split('/').pop();
 58      if(!user_data_to_exclude.includes(cleanName) && value) {
 59        other_properties[cleanName] = value
 60      }
 61    });
 62  } else if (version == 'v3') {
 63    for (var key in fieldValues) {
 64      if (fieldValues.hasOwnProperty(key) && fieldValues[key] && !user_data_to_exclude.includes(key) && key != 'hs_context') {
 65        other_properties[key] = fieldValues[key];
 66      }
 67    }
 68  }
 69
 70  return other_properties;
 71
 72}
 73
 74function normalizeUserData(fieldValues, version) {
 75  var user_data = {};
 76
 77  var fieldMap = {
 78    email: 'email_address',
 79    phone: 'phone_number',
 80    mobilephone: 'phone_number',
 81    firstname: 'address.first_name',
 82    lastname: 'address.last_name',
 83    address: 'address.street',
 84    city: 'address.city',
 85    state: 'address.region',
 86    country: 'address.country',
 87    zip: 'address.postal_code'
 88  };
 89
 90  if(version == 'v4') {
 91    fieldValues.forEach(function(field) {
 92      var name = field.name;
 93      var value = field.value;
 94      var cleanName = name.split('/').pop();
 95      var mappedPath = fieldMap[cleanName];
 96
 97      if (mappedPath && value) {
 98        var keys = mappedPath.split('.');
 99
100        if (keys.length === 1) {
101          user_data[keys[0]] = value;
102        } else if (keys.length === 2) {
103          if (!user_data[keys[0]]) {
104            user_data[keys[0]] = {};
105          }
106          user_data[keys[0]][keys[1]] = value;
107        }
108      }
109    });
110  } else if(version == 'v3') {
111    for (var key in fieldValues) {
112      if (fieldValues.hasOwnProperty(key) && fieldValues[key]) {
113        var mappedPath = fieldMap[key];
114        if (mappedPath) {
115          var keys = mappedPath.split('.');
116
117          if (keys.length === 1) {
118            user_data[keys[0]] = fieldValues[key];
119          } else if (keys.length === 2) {
120            if (!user_data[keys[0]]) {
121              user_data[keys[0]] = {};
122            }
123            user_data[keys[0]][keys[1]] = fieldValues[key];
124          }
125        }
126      }
127    }
128  }
129
130  return user_data;
131}
132/*
133* v0.1.0
134* Created by Data Marketing School at https://www.data-marketing-school.com
135* Written by https://www.linkedin.com/in/lucasrollin/
136*/
137</script>
Custom HTML tag to add the Hubspot form listener
Custom HTML tag to add the Hubspot form listener

I recommend to trigger this tag only on the page(s) that contain Hubspot forms.

Example of a valid form submission

On a valid form submission, the generate_lead event is sent to the Data Layer.

If you have several forms on the same page, you’ll need to use the form_id variable to differentiate them in GTM.

Generate_lead event in Data Layer
Generate_lead event in Data Layer

Create the corresponding Data Layer variables

Form identifier

Retrieve the form_id in the Data Layer
Retrieve the form_id in the Data Layer

Form fields

Here you need to create a variable for each field of your form.

Recover form fields in the Data Layer
Recover form fields in the Data Layer

Going further

You can send the user data collected via your Hubspot form to advertising platforms to improve your performance: