How to track iframes with Google Tag Manager
Loading... iframes in GA using Google Tag Manager

How to track iframes with Google Tag Manager

Jellyfish Senior Data & Analytics Director Max Yodgee explores options for iframe tracking in gtm

If you have worked with a website that uses iframes, then you probably know the tracking pain points that arise. Generally, you’ll encounter one or all of the following:

  • Campaign/Referral attribution loss
  • Session restarts/dropouts
  • Double page views
  • Inaccurate bounce rate
  • Cookie/Browser storage access

The issues you encounter depend on how your iframes are set up; for instance, are they on the same domain, sub-domain or perhaps hosted on a completely different domain? What does the user do at the end of the process? Does a third-party service even allow you to add tracking?

The solution is postMessage. This supported browser functionality allows you to send messages between both the iframe and parent window/main website, in most cases circumventing all the problems you would face traditionally with tracking iframes.

While in essence, it sounds fairly straightforward, there are intricacies with the set-up and it’s development-heavy. So put on your coding cap, and ready your copy and paste, as it's time to fix your iframe tracking.

Please note, this solution assumes that coding changes can be made to your iframe. While the set-up is implemented via Google Tag Manager, the general concept and JavaScript can still be applied directly.

Tracking iframes in GA using Google Tag Manager

The Concept

  • Tracking in the iframe will use postMessage to send data to the main website.
  • The main website will be listening for messages posted from the iframe.
  • The main website will convert these received messages into analytics tracking calls.

The great aspect of this solution is that the iframe simply sends messages to the main website, no tracking scripts are called within the iframe. The main website, where the tracking takes place, will not encounter session, attribution or access issues while providing more control of when to fire specific tracking calls for the iframe content.

The Set-up

  • Google Tag Manager is implemented on the main website.
  • Google Analytics is implemented on the main website.
  • A separate Google Tag Manager container is implemented in the iframe.

The Solution

iFrame setup

  1. Create a Custom HTML Tag: Workspace > Tags > New > Choose tag type > Custom HTML
  2. Copy & paste the below code snippet into the Custom HTML Tag
  3. Add the All Pages Trigger: Tag > Triggering > All Pages
  4. Save & Name the Tag: cHTML - postMessage - Send - Page View

Code snippet:

    try {
        if(typeof parent != "undefined" && parent != window) {
            if(typeof parent.postMessage != "undefined") {
                var message = {};
                message["origin"] = {
                    "type" : "iframe",
                    "host" : {{Page Hostname}},
                var event = "custom.postMessage";
                // Add description of the event
                event += ".page";
                message["event"] = event;
                // Add custom data
                message["url"] = {{Page URL}};
                // Convent message into a string
                var messageJSON = JSON.stringify(message);
                //Send message to parent
                parent.postMessage(messageJSON, "*");
    } catch(err){if({{Debug Mode}}) console.log(err);};


The above code snippet is designed to run within the iframe. It first checks to see if a parent exists (parent reference indicates the document is a child and/or iframe). Then the functionality of postMessage is confirmed to be available. Next a message is created; in this example we are creating a simple page view. The message object is then converted into a string to allow complex data to be passed across and the message is sent.

Main website set-up

  1. Create a Custom HTML Tag: Workspace > Tags > New > Choose tag type > Custom HTML
  2. Copy & paste the below code snippet into the Custom HTML Tag
  3. Add the All Pages Trigger: Tag > Triggering > All Pages
  4. Save & Name the Tag: cHTML - postMessage - Receive

Code snippet:

Code snippet:
  try {
    var receiveMessage = function(event) {
      try {
        if(event && typeof != 'undefined') {
          var message = JSON.parse(;
          if(message && typeof message.event != 'undefined' && message.event.indexOf('custom.postMessage') >= 0) {
            if(dataLayer) {
      } catch(err) {};
    if(typeof window.addEventListener !== 'undefined') {
      window.addEventListener('message', function(e) {
    } else if (typeof window.attachEvent !== 'undefined') {
      window.attachEvent('on' + 'message', function(e) {
  } catch(err){if({{Debug Mode}}) console.log(err);};


This snippet is set up to receive message events. It is important to note that the event listener functionality is not specifically targeted to the iframe events only. It’s possible that these listeners will pick up other message events from existing website functionality.

To counter this potential impact, you can see that within the receive function, conditional checks are used to validate the message received, and only react if it is indeed the event we specified from the iframe.

Upon validating the message, the message data is converted back into an Object and is then pushed into the main website dataLayer.

Completing and Tracking the Set-up

Now that we have the message entering the dataLayer, we simply need to create the required variables, trigger and tag.

Create the Variables

Workspace > Variables > User-Defined Variables > New  

DLV - url

This is used to extract the URL in the dataLayer from the posted message.

Choose variable typeData Layer Variable
Name/TitleDLV - url
Data Layer Variable Nameurl

URL - dataLayer - Path

This is used to extract the page path only from the DLV - url variable.

Choose variable typeURL
Name/TitleURL - dataLayer - Path
Component TypePath
More Settings > URL Source{{DLV - url}}

Create the Trigger

Workspace > Triggers > New  

CE - postMessage - Page This trigger will fire when the dataLayer receives the postMessage.

Choose trigger typeCustom Event
Name/TitleCE - postMessage - Page
Use regex matchingTrue
This trigger fires onAll Custom Events

Create the Google Analytics Tag

Workspace > Tags > New > Google Analytics  

GA4 - page_view - postMessage

Choose tag typeGoogle Analytics: GA4 Event
Name/TitleGA4 - page_view - postMessage
Measurement IDSet the appropriate Measurement ID
Event Namepage_view
Event Settings VariableSelect your Event Settings Variable
Event parameterAdd any appropriate Event parameters as required
Triggering > Choose a triggerCE - postMessage - Page

Final Thoughts

It’s worth noting that postMessage is a two-way street; it's actually possible to pass messages from the main website back to the iframe. You can then think of this set-up in reverse. For example, an iframe sends a message to the main website declaring it's ready. The main website reacts to this message, sending a message back containing information such as the user's client id (cross-domain tracking), instructions to trigger specific events, or other information that needs to be persisted. However, it’s worth mentioning that this approach is still not desired mainly due to restrictions in IOS that prevent the setting of third-party cookies when using iframes with different domains.

While this solution is fairly robust, it does assume that the main website will be loaded and have called the listener scripts before the child iframe loads and sends its message. This should be the case for the majority of set-ups; if you do notice missing pages then it might be worthwhile adding in logic to ensure both the main website and iframe are loaded before attempting to send messages.

This set-up is only scratching the surface of postMessage. In this example, we have only passed a Pageview from the iframe to the main website. It is possible to use the same methodology to track events and other key calls to actions that occur within the iframe. An ultimate solution would be to monitor every dataLayer event within the iframe and pass the messages across to the main website.

While this set-up is advanced, it will drastically improve the data quality and collection. When implemented correctly, it will circumvent many of the issues presented with iframes and provide greater control of how and when tracking occurs.

So, there we have it; a way to track iframes with Google Tag Manager. If you're new to GTM, or want to up your data analysis game, check out our Google Tag Manager training course.

This post first appeared on the Data Runs Deep blog. Data Runs Deep joined the Jellyfish global family in 2020 and the knowledge-sharing continues under the Jellyfish banner in Australia and around the world.

About the author

Max Yodgee

Senior Data & Analytics Director