Integrating Google Analytics 4 with Hubspot - Using Hubspot Form Hidden Field

Integrating data you collect in Google Analytics and the data you collect in Hubspot of the same users can open a door full of insights. This article provides a detailed guide on how to pass Google Analytics 4 Client IDs to HubSpot forms, a key step for integrating GA data with your CRM system. It covers the setup of hidden fields in HubSpot forms, retrieving the Client ID using both Google Tag Manager and Gtag JavaScript code. Even though the article focuses on Hubspot, but the same principles can just as well apply to any other CRM system.
connecting google analytics dataset of user activities with user activities stored in hubspot

To join a user’s data from Google Analytics to any other platform, the single important dimension you need is Client ID. There are hundreds of articles that explain but we’ll give a small refresher.

What is Google Analytics Client ID?

Client ID is a unique value that identifies (pseudo anonymously) a visitor visiting from one browser. That is to say that if the same visitor visits your website from a different browser, a new client ID gets assigned to this visitor. Further, if the visitor deletes the cookies and revisits the website, a new client ID is dispatched.

Visitor A

Chrome > Client ID = 123.231

Safari > Client ID = 345.543

Firefox > Client ID = 678.876

Never mind the length of the client IDs in the above examples. Thats just for showing it in a simple form. Normally a client ID is a long string of number like 1234567890.1234567890 (notice a dot between the two numbers!).

Visitor A deletes cookies from Chrome and revists from all three browsers again

Chrome > Client ID = 999.878

Safari > Client ID = 345.543

Firefox > Client ID = 678.876

The client ID stays the same as long as the visitor keeps coming back from the same browser and does not delete cookies before doing so.

Other visitors will have their own client Ids assigned in their browsers.

Using Client ID to join GA with Hubspot

We’ll see how you can successfully capture a client ID and store it in your CRM systems. We’ll take Hubspot as an example but fundamentally this is method can be used for any other systems with slight adjustments.

The idea is to send the user’s client ID when they submit a form on your website, thus passing the value into your CRM system.

What’s the benefit of storing GA4 Client ID in Hubspot or any other system?

The benefit of storing client IDs in your system is two-fold:

  1. It helps you join Google Analytics 4 dataset with your CRM
  2. Not only that, it’ll also enable you to pass back events (like tracking email opens, or other offline events) from your CRM into your Google Analytics 4 account, thus, essentially creating a two-way street. Needless to say the event you pass back into GA4 gets attached to the same user data in GA4.

The process is simpler than it sounds.

Step 1: Add a hidden field to your Hubspot Form

This is a no-brainer step but if you’d like to store the client ID you need a Hubspot property to do that. In this case, we’ll use a hidden field in our forms and label it as “ga_client_id”

Step 2: Retrieve and update hidden field with the Client ID

How you retrieve client ID depends largely on the method you used to deploy your GA4 tracking code. It could be that you are either using gtag tracking code directly in your pages’ headers OR you are using Google Tag Manager. We’ll discuss both methods:

Choose a scenario

We have covered four scenarios here and you can jump to whatever is most relevant for your use case.

Scenario 1: Hubspot Form Embed Code & Gtag Tacking Code

  • You have access to Hubspot form embed code
  • You use Gtag tracking code on the website

Scenario 2: Using Hubspot Form Embed Code & Google Tag Manager

  • You have access to Hubspot form embed code
  • You use Google Tag Manager on your website

Scenario 3: Using only Google Tag Manager

  • You do not have access to Hubspot form embed code or your page code
  • You use Google Tag Manager on your website and have access to it

Scenario 4 (Rare): You can make changes to page code but not to the Hubspot Form code

  • You have access to your CRM and can make changes to the code
  • You cannot make changes to Hubspot Form Code

Scenario 1: Hubspot Form Embed Code & Gtag Tacking Code

  • You have access to Hubspot form embed code
  • You use Gtag tracking code on the website

First retrieve client ID with GTAG tracking code:

Retrieving client ID with Gtag tracking code is extremely simple since GA4 get command allows you to get the values easily

gtag('get', 'G-XXXXXXXXXXXX', 'client_id', function(client_id) {
         console.log("Retrieved Client ID: ", client_id);
         }
       });

You’ll have to of course replace G-XXXXXXXXXXXX with your own GA4 Measurement ID.

Update Hubspot form hidden field with retrieved Client ID

Next, we’ll use this function in our Hubspot embed code. Modify your hubspot code to the following:

<script charset="utf-8" type="text/javascript" src="//js-eu1.hsforms.net/forms/embed/v2.js"></script>
<script>
 // Load the HubSpot form independently
 document.addEventListener("DOMContentLoaded", function() {
   hbspt.forms.create({
     region: "eu1",
     portalId: "1111111",
     formId: "1111x1x1-1x11-1x11-1111-111x11111x11",
     onFormReady: function(form) {
       var hiddenField = document.querySelector('input[name="ga_client_id"]');
       // Attempt to get the client ID when GA4 is available
       gtag('get', 'G-QVDGX7TRX8', 'client_id', function(client_id) {
         console.log("Fresh Retrieved Client ID: ", client_id);
         if (hiddenField) {
           hiddenField.value = client_id;
         }
       });
     }
   });
 });
</script>

Replace 1111111 with your portal ID
And 1111x1x1-1×11-1×11-1111-111x11111x11 witn your form ID

You’ll notice that we used the Hubspot onFormReady hook to

  • getting the client ID from our GA tracker using the function described above
  • set the value of the hidden field (ga_client_id) added in the previous step to the retrieved client ID

You might want to test it a few times before you take this live.

If your tests goes through, apply this change to all your forms.

Note:
If a user rejects permissions for GA tracking, the form will still load because it loads independently of whether the client is retrieved or not for a smooth user experience. However, if a user has Javascript disabled, then we’re out of luck on both counts!

Scenario 2: Hubspot Form Embed Code & Google Tag Manager

  • You have access to Hubspot form embed code
  • You use Google Tag Manager on your website

Retrieve Client ID from GA cookie and updating Hubspot Form Embed code (no changes needed in Google Tag Manager)

This method can essentially work with Gtag tracking code too. However unlike how we described previously, where we “get” the client ID from the ga tracker, here we retrieve the cookie value from the “ga cookie”.

Side note
Retrieving value from the cookie may not be 100% reliable if your function runs before the cookie is set, so you might have to queue the functions accordingly.

Retrieve the Client ID value from the ga cookie.

We’ll use this function.

function getGA4ClientId() {
    const cookieValue = document.cookie.split('; ').find(row => row.startsWith('_ga='));
    return cookieValue ? cookieValue.split('=')[1].split('.').slice(-2).join(".") : 'not_set';
}

Next we use this function in our Hubspot form embed code as this:

<script charset="utf-8" type="text/javascript" src="//js-eu1.hsforms.net/forms/embed/v2.js"></script>
<script>
 // Function to get GA4 Client ID
 function getGA4ClientId() {
   const cookieValue = document.cookie.split('; ').find(row => row.startsWith('_ga='));
   return cookieValue ? cookieValue.split('=')[1].split('.').slice(-2).join(".") : 'not_set';
 }

 hbspt.forms.create({
   region: "eu1",
   portalId: "1111111",
   formId: "1111x1x1-1x11-1x11-1111-111x11111x11",
   onFormReady: function(form) {
     
// This function is called when the form is fully rendered
     var clientId = getGA4ClientId();
     
// Set the value of the hidden field (assuming 'form_transaction_id' is the correct name)
     var hiddenField = document.querySelector('input[name="ga_client_id"]');
     console.log(clientId);
     if (hiddenField) {
       hiddenField.value = clientId;
     }
   }
 });
</script>

Replace 1111111 with your portal ID
And 1111x1x1-1×11-1×11-1111-111x11111x11 witn your form ID

As you’ll see, in this function the fall back value is ‘not set’. This will also be a way for you to figure out the success of this implementation. A percentage of ‘not set’ values from the total contacts will give you the success rate of this method. Moreover, it also provides you an insight on how many users consent to GA4 tracking.

Scenario 3: Using only Google Tag Manager

  • You do not have access to Hubspot Code
  • You use Google Tag Manager on your website
  • You have access to Google Tag Manager

We do the same thing as above here, just using GTM.

Retrieving GA Client ID from GTM 1st Party Cookie Variable

1. Create a new 1st party Cookie Variable. You can call it ‘ga Cookie’.

2. Create a second variable to extract the Client ID from this cookie

Create a new custom javascript variable, call it Client ID, and add the following code to it

function() {
return {{ga cookie}}.substring(6)
}

Go to GTM preview mode and test if you see the Client ID value in your variables list on a simple page view.

Update Hubspot Form Hidden Field via GTM

Next you’ll take this client ID and update the value of your GA Client ID hidden field in the Hubspot form. 

The biggest problem when setting a hidden field in Hubspot form through GTM is the timing. Sometimes the GTM tag will load first and will not update the hidden field. Other times, it may do just as you’d like. To counter this, instead of adding a simple JS that updates the hidden field (like we did in our Hubspot embed code), the approach here would be to “observe” if the form has first loaded and only then update the hidden field.

Create a custom HTML tag and set the firing trigger to be the last of in the trigger sequences of GTM like “Window Loaded”. As for other conditions, you could either simply fire it on all pages or add specific condiutions so the tag fires only on pages where a form is available

Add this Javascript to it

<script>
// Target the body or specific container where the form will be loaded
var targetNode = document.body;

// Create an observer instance
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        var inputField = document.querySelector('input[name="ga_client_id"]');
        if (inputField) {
            inputField.value = {{client ID}};
            console.log("Client ID set:", {{client ID}});
            observer.disconnect(); // Stop observing once we've set the value
        }
    });
});

// Configuration of the observer:
var config = { childList: true, subtree: true };

// Start observing
observer.observe(targetNode, config);
</script>

This script will actively monitor changes in the body (or another specified container), checking if the hidden field becomes available and setting its value as soon as it does. This approach is particularly useful for dynamically loaded content.

To know more about mutation observers check out the documentation – https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

CAVEAT

If the user deletes their cookies, a new client ID will get generated. So the same user may have 2 different client IDs stored on two different forms. The latest client ID will overwrite the previous one. You will be able to see the difference if you’ve deployed a data warehouse. However, for sending offline events back into GA4, this won’t matter as the event will get associated with the latest client ID of the user.

Scenario 4 (Rare): You can make changes to page code but not to the Hubspot Form code. What to do?

  • You have access to your CRM and can make changes to the code
  • You cannot make changes to Hubspot Form Code
  • Works for both GTag tracking code and Google Tag Manager

It is possible that you have access to the page code but not to the Hubspot Form code – perhaps you are using a Hubspot extension in your CRM that directly embeds the form into the page. In such cases, you will combine the above methods. We’ll assume that you still have an access level that allows you to add Javascripts to your page. Without that, you’re just out of luck.

So you add the first Javascript that extracts the GA4 Client ID.

Then you add the JS we used in our custom HTML tag (descried above) but a small, yet significant change. Since this won’t be used in GTM, the GTM variable we refer to in this script won’t be available. Here is how the script would look like:

<script>

function getGA4ClientId() {
    const cookieValue = document.cookie.split('; ').find(row => row.startsWith('_ga='));
    return cookieValue ? cookieValue.split('=')[1].split('.').slice(-2).join(".") : 'not_set';
}
  var clientId = getGA4ClientId();

// Target the body or specific container where the form will be loaded
var targetNode = document.body;

// Create an observer instance
var observer = new MutationObserver(function(mutations) {

    mutations.forEach(function(mutation) {
        var inputField = document.querySelector('input[name="ga_client_id"]');

        if (inputField) {
            inputField.value = clientId
            console.log("Client ID set:", clientId);
            observer.disconnect(); // Stop observing once we've set the value
        }
    });
});

// Configuration of the observer:
var config = { childList: true, subtree: true };

// Start observing
observer.observe(targetNode, config);
</script>

Closing note: Custom Dimension in GA4 and High Cardinality

So far, what we did is – pass client IDs to Hubspot. But in order to create a connection between the two datasets, we need the client ID in GA4 too – which will then become the “join” key. If you have the join key missing in one dataset, there won’t be a join 😉

By default, GA4 does not store client ID. Its available in Big Query though. So unless you enable Big Query for your Google Analytics account – you’ll have to set up client IDs storage into a custom dimension.

A word of caution here – since client IDs are unique to every visitor, this custom dimension is quite prone to “High Cardinality” (i.e. dimensions with more than 500 unique values in one day.). If you have more than 500 visitors per day, you will most likely run into High Cardinality issues. The problem with high cardinality is when you look at GA reports, it will sample your data or aggregate most of it into an “Other” category (something quite frustrating).

Note that you need client IDs stored only for form submissions because users who submit the forms are the ones you will be able to connect with Hubspot – not all of them. Therefore, you could just send the client ID value to your custom dimensions on only form submit events. Once you have the client ID in – you should be able to create segments or audiences in GA4 to fetch the rest of the user behavior.

Still, for those seeking a seamless and robust integration, enabling BigQuery is highly recommended.

And there you have it—various methods for capturing and sending client IDs to HubSpot contact properties, regardless of whether you have access to the HubSpot embed code.

1 Comment

    Leave a Reply