Register for our exclusive VIP Event on May 21st, limited spots available!
Services
Consultancy Project-Based Approach Support Training Academy
Accelerators
AI Data Analyst Artificial Intelligence CDP as a Service Customer Data Architecture Data Ideation Workshops Data Maturity Assessment Job Analytics Server Side Tracking
Cases
News
Events
About
Our Values Our Team Our Toolkit Careers
Contact Us
  • News
7 October 2019
5 min read

Tracking iframes with Tealium

Introduction text to the article. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam condimentum dolor eu ex pretium, non dignissim leo placerat. Donec luctus dictum semper. Donec est diam, semper at arcu efficitur, malesuada hendrerit enim. Aliquam dictum lacus id vehicula consectetur. Nunc dapibus id ante at suscipit. Phasellus iaculis enim sed dui scelerisque, et lobortis lectus egestas. Duis eu eros arcu. In et odio semper massa scelerisque laoreet

Sam Van Renterghem
Analytics Implementation Expert Sam Van Renterghem

Approach

Initially, you may consider adding Tealium to your iframe, and making sure that it is sending hits to all the tags you have added to your profile. However, we'll be using a different approach. We still need to add Tealium to the iframe, but we're going to use Tealium to push pageviews and events to the parent page (the page hosting the iframe). So let's say page A is loading iframe B, but instead of iframe B sending hits to tags directly, it will send hits to page A, and page A will then send the hits to the tags configured in that Tealium profile. This way, you will only have your tags configured in one profile, instead of your tags being spread across multiple profiles.

There are two options for your Tealium setup: either you use the same Tealium profile on your website and the iframe, or you use a separate Tealium profile in the iframe (recommended).

The problem with using the same profile is that iframes will likely be loaded on multiple domains for different websites. Even if that's not the case, you'll have to constantly consider the iframe when building load rules or adding tags, since you don't want your tags to load within the iframe.

Setup

Let's assume we're using 2 different Tealium profiles: the "parent" profile will be loaded on the main website (or parent), and the iframe (or child) will be using "child" profile. We will need to make changes in both profiles to make the tracking work.

We have already determined that we will use the child profile to send events to the parent profile. So the parent profile will have 2 tasks:

  • Receiving events from the child profile (iframe).

  • Letting the child profile know that it is ready to receive events.

This first task is obvious, but let's see why this second task is necessary. When a website is loading an iframe, it is possible that the iframe will finish loading before the website itself. This means that the child profile would load before the parent profile. Of course, this is not always the case, it is also possible for the parent profile to load before the child profile. This is important to consider because the child profile should not send events to the parent profile before it is ready to receive them.

The child profile will also have some tasks it needs to accomplish:

  • Measuring events.

  • Waiting for the parent profile to be ready to receive events.

  • Sending events to the parent profile.

Of course, we need to measure events. This could be as simple as measuring pageviews, but can be extended to any event like form tracking, button clicks, etc.. When an event occurs, we have to be sure that the parent profile is ready to receive them, so we must wait if necessary. When the parent profile is ready, we just need to push the event to the parent profile, and we're finished.

Code: the child profile (iframe)

All of these tasks can be boiled down to two JavaScript extensions. One extension on the parent profile, and one extension on the child profile. Let's have a look at the extension for the child profile. We'll start from the child profile because that's where the events are generated:

//Child (iframe) extension
(function() {
    // Configuration
    var PARENT_ORIGIN = 'https://parent.example.org';
    var SEND_PAGEVIEW = true;

    // ==============================================================
    // Do not edit below this line unless you know what you're doing.
    // ==============================================================
    var pageviewSent = false;
    var started = false;
    var eventQueue = [];
    window.utag_iframe = {
        view: onView,
        link: onLink
    };
    if(SEND_PAGEVIEW){
        eventQueue.push(utag_data);
    }

    window.addEventListener('message', onMessage, true);
    sendHandshake();

    function onView(dataLayer){
        eventQueue.push({
            event: 'view',
            data: dataLayer
        });
    }

    function onLink(dataLayer){
        eventQueue.push({
            event: 'link',
            data: dataLayer
        });
    }

    function sendEvent(event) {
        var data = {
            tealium_event: event
        };
        window.parent.postMessage(data, PARENT_ORIGIN || '*');
    }

    function onMessage(message){
        if(message.data && message.data.tealium_handshake === 'parent'){
            startSendingEvents();
        }
    }

    function startSendingEvents(){
        if(started){
            return;
        }
        started = true;

        eventQueue.push = function(event){
            sendEvent(event);
        };
        for(var i=0; i<eventQueue.length; i++){
            (function(event){
                setTimeout(function(){
                    sendEvent(event);
                }, i*100);
            })(eventQueue[i]);
        }
    }

    function sendHandshake(){
        var data = {
            tealium_handshake: 'child'
        };
        window.parent.postMessage(data, PARENT_ORIGIN || '*');
    }
})();

I know, it's quite a handful... Don't be alarmed though, this code takes care of a bunch of things for you, so you don't have to get your hands too dirty! At the top of the code, however, there are 2 things you still have to adjust. Let's check it out:

var PARENT_ORIGIN = 'https://parent.example.org';
In this line, simply replace the URL with the URL of the parent website, but do not include the page path.

Additionally you can choose not to send the initial page view (based on utag_data) when the iframe loads by setting SEND_PAGEVIEW to false. Simply replace the words "true" with "false" (without quotes).

Code: The parent profile

Now that the extension is set up to send events to the parent profile, let's jump into the parent profile's extension to receive the events and send them on their way to our favourite tags:

//Parent extension
(function() {
    // Configuration
    var CHILD_ORIGIN = 'https://child.example.org';
    var IFRAME_ID = 'my-iframe'; //HTML ID of the iframe

    // ==============================================================
    // Do not edit below this line unless you know what you're doing.
    // ==============================================================
    window.addEventListener('message', function(message) {
        var data = message.data;
        if (data.tealium_event) {
            utag.track(data.tealium_event);
        }else if(data.tealium_handshake === 'child'){
            sendHandshake();
        }
    }, true);
    sendHandshake();

    function sendHandshake(){
        var iframe = document.getElementById(IFRAME_ID);
        var data = {
            tealium_handshake: 'parent'
        };
        iframe.contentWindow.postMessage(data, CHILD_ORIGIN);
    }
})();

This code will simply listen for any events coming from the iframe, and trigger utag.view or utag.link accordingly. Here too, you have to set up some configuration:

At the top of this code, you should set the value of CHILD_ORIGIN to the URL of the iframe, just like you've done for PARENT_ORIGIN in the iframe extension. These origin configurations are necessary to make sure that malicious sites loading your website will not receive any events.

You also have to set the IFRAME_ID to the HTML ID of your iframe. So in the HTML you should see something like id="some-iframe" for your iframe. In this case you should set the variable as IFRAME_ID="some-iframe".

Finishing up

Once these configurations are done, you're free to publish your setup. However, if you chose not to send the initial pageview from the iframe, no hits will be sent yet. Within the child profile you should still add events if you want to track them, but instead of pushing them to utag.view and utag.link, push them to utag_iframe.view and utag_iframe.link. These are 2 functions that were created in the child extension within the iframe. If you're not familiar with JavaScript, this is something your website developer will have to do.

As always, make sure you test that everything is working before publishing to production.

Ready to activate your data?

Ready to embark on a journey of success? Contact us today to discuss your needs. Let's work together to turn your vision into reality.

Reach out, and let's chat.
pencil drawing of two men
  • Contact us
  • Hertshage 10
    9300 Aalst, Belgium
  • welcome@multiminds.eu
  • +32 491 33 11 11
  • Our services
  • Consultancy
  • Project-Based Approach
  • Support
  • Training
  • Our accelerators
  • CDP as a Service
  • Customer Data Architecture
  • Data Ideation Workschops
  • Data Maturity Assessment
  • Server Side Tracking
  • Job Analytics
  • AI Data Analyst
  • Artificial Intelligence
  • Our newsletter
  • Subscribe to our newsletter for the latest news and upcoming workshops.
  • Thank you for subscribing!

©2026 MultiMinds. All rights reserved.

Cookie Policy Privacy Policy

We’re an analytics agency. You know what’s coming.

Honestly? We just want to see how you move through our site so we can make our charts look beautiful and our insights even sharper. It's like a science experiment, and you're our favourite variable.

Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.

anonymous
2 year | HTTP Cookie
Stores the user's cookie consent state for the current domain.
_cfuvid
Session | HTTP Cookie
This cookie is a part of the services provided by Cloudflare - Including load-balancing, deliverance of website content and serving DNS connection for website operators.
cf.turnstile.u
Persistent | Local Storage
This cookie is used to distinguish between humans and bots.

Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.

analyticsplatformassistant
n/a | Local Storage
Persists user-selected preferences and progress for the Analytics Platform Wizard.

Analytical cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.

_ga#
1 year | HTTP Cookie
This cookie is a Google Analytics persistent cookie which is used to distinguish unique users.

Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.