

How to Track iFrame Content with GTM and GA4 Using postMessage
This guide explains how to use postMessage to track iframe interactions in Google Tag Manager without duplicate tags or broken client IDs

Rob English
Lead Implementation Specialist
A privacy-focused web analytics implementation specialist, with 19 years experience in development across marketing, advertising and analytics.
Tracking user interactions inside iFrames can be a challenge, especially when those iFrames come from third-party sources. Whether it's a form, a video, or an embedded tool, content delivered through an iFrame runs in a separate environment from your main webpage. That means your standard Google Tag Manager (GTM) setup won’t see what happens inside. But there’s a smart workaround: using the postMessage method for cross-window communication.
In this blog, we’ll explore the challenges of iFrame tracking, the common pitfalls of duplicating GTM tags, and a clean solution that keeps your data accurate and connected—all while using just one GTM container on the parent page.
Why iFrames Are Difficult to Track
When you use an iFrame, you're embedding content from a third-party website or domain such as video or a form. Even if it looks like everything is part of one page, the browser sees the iFrame as separate. This separation makes it nearly impossible for the parent window to directly access or track anything happening inside the iFrame.
For example, if you’ve set up GTM on the main (parent) page, it won’t detect events or clicks that happen inside the iFramed area. That’s because the iFrame runs on a different domain, which blocks direct communication between the two.
Why Adding GTM to the iFrame Isn’t a Great Fix
One quick fix might seem like adding another GTM container directly into the iFramed content . While that allows you to tag interactions inside the iFrame, it creates more problems than it solves:
Duplicate tracking: Tags may fire from both the parent and child containers for the same interaction, which can lead to overreporting.
Different client IDs: Since the parent and iFramed pages live on separate domains, they each assign a different GA4 client ID. A single user could show up as two different users in your reports.
Confusing page data: Events from the iFrame report under its domain, not the parent page, which fragments your reporting and makes analysis harder.
In short, using separate GTM containers breaks the user journey into disconnected pieces, making it harder to understand how visitors are really interacting with your site.
A Better Approach: postMessage Communication
Instead of duplicating GTM, you can bridge the gap between the parent and iFramed content using a built-in JavaScript method called postMessage()
. This method allows two windows from different domains (like your main site and an iFrame) to send messages to each other safely.
Here’s how it works.
On the Parent Page
he following code should be added to the <head> of the parent page, in which we’ll embed our iFrame.
There are two key components to this script.
The addEventListener
portion of code will listen for incoming messages from our iframe. It HAS to be an iframe with an origin of the hostname of the iFramed page. This extra check for the origin can help prevent either accidental or intentional (and possibly malicious) pushes or interactions with the parent page from any iframe that may be embedded in our parent domain.
It will then take the event.data object from the message, and run the buildData
function.
The buildData
function can be used to push our event messages onto our parent level dataLayer. I’ve set this up as a separate function so that, at the parent page level we can pull apart the event.data and piece it back together however we need prior to the event being pushed to our dataLayer.
<script>function buildData(n) {
window.dataLayer = window.dataLayer || [];
dataLayer.push(n);
}wind
ow.addEventListener(
"message",
(event) => {
if (event.origin !== "[IFRAMED DOMAIN HERE]") return;
// additionally buildData(event.data) could be replaced with any pre-existing event method to push data on to the dataLayer
buildData(event.data);
},
false,
);</script>
This script does two things:
It listens for messages sent from the iFrame.
When a message arrives, it pushes the data to the parent page’s dataLayer.
This setup helps you track events inside the iFrame without needing GTM in the iFramed content.
On the iFramed Page
Add the following JavaScript code inside the iFramed page’s
// pushToParent = function to communicate with parent window
function pushToParent(message) {
parent.postMessage(message, "[PARENT WINDOW DOMAIN AND PAGE PATH]");
}
// buildEventHandlers = function to create event handlers for requested eventing
function buildEventHandlers() {
/*The code within buildEventHandlers is an example of how we would then implement an Event Listener to the iFramed content, to communicate events. This code isn't a requirement but is being included as an example of how the whole of the code will function.*/
let eventMessage;
let anchorTags = document.querySelectorAll("a");
for (anchor of anchorTags) {
anchor.addEventListener("click", function(e) {
eventMessage = {
'event' : 'link_click_pageflow',
'click_url' : this.href,
'click_text' : this.innerText,
'click_page_location' : document.location.href
}
pushToParent(eventMessage);
});
}
// Listen for DOMContentLoaded before adding listeners
document.addEventListener("DOMContentLoaded", function () {
buildEventHandlers();
});
This code listens for click events on links inside the iFrame. When a user clicks a link, it sends a message to the parent page with the link’s details. The parent page then pushes that data into its GTM dataLayer.
Here’s what the resulting dataLayer push looks like on the parent window:
dataLayer.push = ({
'event' : 'link_click_pageflow',
'click_url' : this.href,
'click_text' : this.innerText,
'click_page_location' : document.location.href
});
This means you can trigger tags, set up variables, and fire events—all from the parent GTM container.
Why This Solution Works
Using the postMessage
method to connect your iFramed content with the parent page has several key advantages:
You only need one GTM container
All tracking can be handled from the parent page, so there's no need to embed a second GTM container in the iFramed content.You keep unified client and session data
Events are tracked under the parent domain’s GA4 configuration. This ensures that Client IDs and session IDs stay consistent, giving you a clearer view of the user journey.You get accurate, centralized reporting
All events appear under the parent domain, which keeps your analytics clean and avoids confusion caused by separate iFramed domain data.It’s safe for cross-domain communication
Theevent.origin
check makes sure only authorized domains can send messages to your page, helping you avoid accidental or malicious data pushes.
Implementation Requirements
To use this method, you’ll need developer access to both the parent and iFramed pages. The code must be added to each side of the setup, so coordination between teams or platforms is necessary.
By using the postMessage method instead of duplicating GTM containers, you can capture interactions inside iFrames without losing sight of the full customer journey. It’s a simple but powerful way to bring everything together under one tracking system—no matter where your content lives.
More Insights


Google Ads and Analytics AI Agents: Smarter Marketing or Risky Move?

Monika Boldak
Associate Director, Marketing
Jun 3, 2025
Read More


Server-Side Tagging (sgtm) with Google Tag Manager: What Marketers and Developers Need to Know

Ricardo Cristofolini
Senior Implementation Specialist, Data Solutions
May 28, 2025
Read More


Declutter Your GTM: A Practical Guide to Cleaning Up Tags, Triggers, and Variables

Ricardo Cristofolini
Senior Implementation Specialist, Data Solutions
May 21, 2025
Read More
More Insights
Sign Up For Our Newsletter

Napkyn Inc.
204-78 George Street, Ottawa, Ontario, K1N 5W1, Canada
Napkyn US
6 East 32nd Street, 9th Floor, New York, NY 10016, USA
212-247-0800 | info@napkyn.com

How to Track iFrame Content with GTM and GA4 Using postMessage
This guide explains how to use postMessage to track iframe interactions in Google Tag Manager without duplicate tags or broken client IDs

Rob English
Lead Implementation Specialist
A privacy-focused web analytics implementation specialist, with 19 years experience in development across marketing, advertising and analytics.
Tracking user interactions inside iFrames can be a challenge, especially when those iFrames come from third-party sources. Whether it's a form, a video, or an embedded tool, content delivered through an iFrame runs in a separate environment from your main webpage. That means your standard Google Tag Manager (GTM) setup won’t see what happens inside. But there’s a smart workaround: using the postMessage method for cross-window communication.
In this blog, we’ll explore the challenges of iFrame tracking, the common pitfalls of duplicating GTM tags, and a clean solution that keeps your data accurate and connected—all while using just one GTM container on the parent page.
Why iFrames Are Difficult to Track
When you use an iFrame, you're embedding content from a third-party website or domain such as video or a form. Even if it looks like everything is part of one page, the browser sees the iFrame as separate. This separation makes it nearly impossible for the parent window to directly access or track anything happening inside the iFrame.
For example, if you’ve set up GTM on the main (parent) page, it won’t detect events or clicks that happen inside the iFramed area. That’s because the iFrame runs on a different domain, which blocks direct communication between the two.
Why Adding GTM to the iFrame Isn’t a Great Fix
One quick fix might seem like adding another GTM container directly into the iFramed content . While that allows you to tag interactions inside the iFrame, it creates more problems than it solves:
Duplicate tracking: Tags may fire from both the parent and child containers for the same interaction, which can lead to overreporting.
Different client IDs: Since the parent and iFramed pages live on separate domains, they each assign a different GA4 client ID. A single user could show up as two different users in your reports.
Confusing page data: Events from the iFrame report under its domain, not the parent page, which fragments your reporting and makes analysis harder.
In short, using separate GTM containers breaks the user journey into disconnected pieces, making it harder to understand how visitors are really interacting with your site.
A Better Approach: postMessage Communication
Instead of duplicating GTM, you can bridge the gap between the parent and iFramed content using a built-in JavaScript method called postMessage()
. This method allows two windows from different domains (like your main site and an iFrame) to send messages to each other safely.
Here’s how it works.
On the Parent Page
he following code should be added to the <head> of the parent page, in which we’ll embed our iFrame.
There are two key components to this script.
The addEventListener
portion of code will listen for incoming messages from our iframe. It HAS to be an iframe with an origin of the hostname of the iFramed page. This extra check for the origin can help prevent either accidental or intentional (and possibly malicious) pushes or interactions with the parent page from any iframe that may be embedded in our parent domain.
It will then take the event.data object from the message, and run the buildData
function.
The buildData
function can be used to push our event messages onto our parent level dataLayer. I’ve set this up as a separate function so that, at the parent page level we can pull apart the event.data and piece it back together however we need prior to the event being pushed to our dataLayer.
<script>function buildData(n) {
window.dataLayer = window.dataLayer || [];
dataLayer.push(n);
}wind
ow.addEventListener(
"message",
(event) => {
if (event.origin !== "[IFRAMED DOMAIN HERE]") return;
// additionally buildData(event.data) could be replaced with any pre-existing event method to push data on to the dataLayer
buildData(event.data);
},
false,
);</script>
This script does two things:
It listens for messages sent from the iFrame.
When a message arrives, it pushes the data to the parent page’s dataLayer.
This setup helps you track events inside the iFrame without needing GTM in the iFramed content.
On the iFramed Page
Add the following JavaScript code inside the iFramed page’s
// pushToParent = function to communicate with parent window
function pushToParent(message) {
parent.postMessage(message, "[PARENT WINDOW DOMAIN AND PAGE PATH]");
}
// buildEventHandlers = function to create event handlers for requested eventing
function buildEventHandlers() {
/*The code within buildEventHandlers is an example of how we would then implement an Event Listener to the iFramed content, to communicate events. This code isn't a requirement but is being included as an example of how the whole of the code will function.*/
let eventMessage;
let anchorTags = document.querySelectorAll("a");
for (anchor of anchorTags) {
anchor.addEventListener("click", function(e) {
eventMessage = {
'event' : 'link_click_pageflow',
'click_url' : this.href,
'click_text' : this.innerText,
'click_page_location' : document.location.href
}
pushToParent(eventMessage);
});
}
// Listen for DOMContentLoaded before adding listeners
document.addEventListener("DOMContentLoaded", function () {
buildEventHandlers();
});
This code listens for click events on links inside the iFrame. When a user clicks a link, it sends a message to the parent page with the link’s details. The parent page then pushes that data into its GTM dataLayer.
Here’s what the resulting dataLayer push looks like on the parent window:
dataLayer.push = ({
'event' : 'link_click_pageflow',
'click_url' : this.href,
'click_text' : this.innerText,
'click_page_location' : document.location.href
});
This means you can trigger tags, set up variables, and fire events—all from the parent GTM container.
Why This Solution Works
Using the postMessage
method to connect your iFramed content with the parent page has several key advantages:
You only need one GTM container
All tracking can be handled from the parent page, so there's no need to embed a second GTM container in the iFramed content.You keep unified client and session data
Events are tracked under the parent domain’s GA4 configuration. This ensures that Client IDs and session IDs stay consistent, giving you a clearer view of the user journey.You get accurate, centralized reporting
All events appear under the parent domain, which keeps your analytics clean and avoids confusion caused by separate iFramed domain data.It’s safe for cross-domain communication
Theevent.origin
check makes sure only authorized domains can send messages to your page, helping you avoid accidental or malicious data pushes.
Implementation Requirements
To use this method, you’ll need developer access to both the parent and iFramed pages. The code must be added to each side of the setup, so coordination between teams or platforms is necessary.
By using the postMessage method instead of duplicating GTM containers, you can capture interactions inside iFrames without losing sight of the full customer journey. It’s a simple but powerful way to bring everything together under one tracking system—no matter where your content lives.
More Insights

How to Track iFrame Content with GTM and GA4 Using postMessage

Rob English
Lead Implementation Specialist
Jun 4, 2025
Read More

Google Ads and Analytics AI Agents: Smarter Marketing or Risky Move?

Monika Boldak
Associate Director, Marketing
Jun 3, 2025
Read More

Server-Side Tagging (sgtm) with Google Tag Manager: What Marketers and Developers Need to Know

Ricardo Cristofolini
Senior Implementation Specialist, Data Solutions
May 28, 2025
Read More
More Insights
Sign Up For Our Newsletter
