Lesson 2 of 3
Iframe PostMessage Bridge
① Connect your GTM container
Paste your container ID to load it into this page. It only ever runs here.
Advanced: use a specific environment
An iframe is a separate document. The widget inside it, a booking calendar, a chat box, a payment form, can't reach your page's dataLayer or GTM directly: browsers isolate cross-frame access for security. So a click inside the embed is invisible to your container.
The bridge is postMessage. The iframe posts a structured message to its parent; a small listener on your page receives it, validates the origin, and relays it into the dataLayer as a normal event. From there it's the usual loop: a Custom Event trigger and a tag.
The embedded widget below posts iframe_cta when its button is clicked. Build the relay listener and tag the event. In production, never skip the event.origin check.
Goal
Relay an iframe's event into the parent dataLayer with postMessage and fire a tag on it.
Build it in GTM
Listener: Custom HTML - Iframe Bridge
- Go to Tags → New → Custom HTML.
- Add a message listener that validates the origin, then relays the event:
<script>window.addEventListener('message',function(e){if(e.origin!=='https://widget.example.com')return;if(e.data&&e.data.type==='ht_iframe'){window.dataLayer.push({event:e.data.event,source:e.data.source});}});</script> - Trigger it on All Pages, name it
Custom HTML - Iframe Bridgeand Save.
Trigger: Custom Event - Iframe CTA
Add a Custom Event trigger oniframe_cta, name itCustom Event - Iframe CTAand Save.Tag: a GA4 Event tag (or Custom HTML - Test)
Add a tag firing onCustom Event - Iframe CTA, readingsourceas a parameter, then Save.
Debug in Tag Assistant
Copy this lesson's live URL and paste it into GTM Preview, that is the page Tag Assistant connects to. It has the clickable elements, so this page stays clean for reading.
- In your GTM, click Preview.
- Paste the live URL above and click Connect.
- Interact with the live page and watch your tag fire in Tag Assistant.
What you should expect to see
iframe_cta to the parent dataLayer and your tag fires, carrying source: embedded_widget.Verify your container
Built it? Export your container, Admin → Export Container, choose your workspace, then drop the JSON here to check it against this lesson.
Drop your container .json here
or browse · checked in your browser, nothing is uploaded