How to track iframes with Google Tag Manager
01 Dec, 2021 Read Time: 8 Minutes
If you have worked with a website that uses iframes then it's likely you have felt the tracking pain points that arise. Generally, you will 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 3rd 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 setup and it is development heavy, so put on your coding cap, 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 setup is implemented via Google Tag Manager, the general concept and JavaScript can still be applied directly.
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 Setup
- Google Tag Manager 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
- Create a Custom HTML Tag
- Worksapce > Tags > New > Choose tag type > Custom HTML
- Copy & paste the below code snippet into the Custom HTML Tag
- Add the All Pages Trigger
- Tag > Triggering > All Pages
- Save & Name the Tag
- cHTML - postMessage - Send - Page View
Code snippet:
<script> (function(){ 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);}; })(); </script>
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 setup
- Create a Custom HTML Tag
- Worksapce > Tags > New > Choose tag type > Custom HTML
- Copy & paste the below code snippet into the Custom HTML Tag
- Add the All Pages Trigger
- Tag > Triggering > All Pages
- Save & Name the Tag
- cHTML - postMessage - Receive
Code snippet:
<script> (function(){ try { var receiveMessage = function(event) { try { if(event && typeof event.data != 'undefined') { var message = JSON.parse(event.data); if(message && typeof message.event != 'undefined' && message.event.indexOf('custom.postMessage') >= 0) { if(dataLayer) { dataLayer.push(message); } } } } catch(err) {}; }; if(typeof window.addEventListener !== 'undefined') { window.addEventListener('message', function(e) { receiveMessage(e); }); } else if (typeof window.attachEvent !== 'undefined') { window.attachEvent('on' + 'message', function(e) { receiveMessage(e); }); } } catch(err){if({{Debug Mode}}) console.log(err);}; })(); </script>
This snippet is setup to receive message events. It is important to note the the event listener functionality is not specifically targeted to the iframe events only. It is possible that these listeners will pick up other message events from existing website functionality.
To counter this potential impact, you can see 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 Setup
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.
Configuration |
Value |
Choose variable type |
Data Layer Variable |
Name/Title |
DLV - url |
Data Layer Variable Name |
url |
URL - dataLayer - Path
This is used to extract the page path only from the DLV - url variable.
Configuration |
Value |
Choose variable type |
URL |
Name/Title |
URL - dataLayer - Path |
Component Type |
Path |
More Settings > URL Source |
{{DLV - url}} |
Create the Trigger
Workspace > Triggers > New
Custom - postMessage - Page
This trigger will fire when the dataLayer receives the postMessage.
Configuration |
Value |
Choose trigger type |
Custom Event |
Name/Title |
Custom - postMessage - Page |
Event name |
custom.postMessage.page |
Use regex matching |
True |
This trigger fires on |
All Custom Events |
Create the Google Analytics Tag
Workspace > Tags > New
GA - Pageview - Virtual - postMessage
Configuration |
Value |
Choose tag type |
Google Analytics - Universal Analytics |
Name/Title |
GA - Pageview - Virtual - postMessage |
Track Type |
Pageview |
Google Analytics settings |
Select your Google Analytics settings variable |
Enable overriding settings in this tag |
True |
More Settings > Fields to Set > Field Name |
page |
More Settings > Fields to Set > Field Value |
{{URL - dataLayer - Path}} |
Triggering > Choose a trigger |
Custom - postMessage - Page |
Final Thoughts
It is 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 setup 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 3rd 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 setups, 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 setup 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 setup 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. Some how 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.











