Skip to content

Instantly share code, notes, and snippets.

@ak--47
Created March 10, 2023 18:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ak--47/a462dcbbc81d23b91918cf123671f578 to your computer and use it in GitHub Desktop.
Save ak--47/a462dcbbc81d23b91918cf123671f578 to your computer and use it in GitHub Desktop.
GA4 / UA → Mixpanel (auto-binding)

GOAL: leverage existing GTM data in Mixpanel

essentially: bind mixpanel.track() calls to existing Universal Analytics + GA4 implementations without adding new triggers for each event

note: this is experimental... every product's GTM implementation is different... please test this in a sandbox before using in production

Step 1: Install the Mixpanel GTM Template to your GTM container


this module binds all of mixpanel's javascript SDK methods to an object that is native to the GTM container.

Step 2: Configure mixpanel.init() to run at consent initialization


  • add a new tag, choose mixpanel... in the tag type dropdown, choose set_config and in the Project Token dialog paste in your mixpanel project token (mixpanel settings => project settings => access keys)



  • for the required set_config option choose:
persistence : localStorage


  • for firing triggers, choose consent initialization


  • we do this because we want to ensure mixpanel is available as soon as possible... we want it at the "top" of the GTM data layer, so it can listen to initial page view events

Step 3: Configure the following HTML snippet to run immediately after the above init tag


this is the code:

<script>
/**
    title: GTM → Mixpanel (auto capture)
    purpose: capture GTM's dataLayer as mixpanel events
    requires: mixpanel installed on page
    by: AK <ak@mixpanel.com>
    governance: see eventBlackList, propertyKeyBlackList, & propertyValueBlackList
*/

(function() {
    if (!window.mixpanel_gtm_dataCapture) {
        try {
            //ensure code only runs once
            window.mixpanel_gtm_dataCapture = true

            //events that CONTAIN the following names WILL NOT be tracked
            var eventBlackList = ['gtm.init', 'gtm.dom', 'gtm.load', 'gtm.scrollDepth', 'gtm.click', 'optimize.']

            //events that HAVE the following properties KEYS WILL NOT be tracked
            var propertyKeyBlackList = ['foo', 'bar', 'baz']

            //events that HAVE the following properties VALUES WILL NOT be tracked
            var propertyValueBlackList = ['qux', 'mux', 'dux']

            //copy gtm's data layer
            var proxied = window.dataLayer.push;

            //patch a function which knows how to track to mixpanel when items are pushed onto the data layer
            window.dataLayer.push = function() {
                var args = Array.prototype.slice.call(arguments);
                var _this = this;
                var gtagData = arguments[0]
                if (gtagData.event && window.mixpanel) {
                    //event is not blacklisted
                    if (!doesContain(eventBlackList, gtagData.event)) {
                        //event does not have blacklisted property KEYS
                        if (unionArr(Object.keys(gtagData), propertyKeyBlackList).length === 0) {
                            //event does not have blacklisted property VALUES
                            if (unionArr(Object.values(gtagData), propertyValueBlackList).length === 0) {
                                //send the event to mixpanel
                                mixpanel.track(gtagData.event, cleanObject(gtagData))
                            }
                        }
                    }

                    //gtm proceeds as normal
                    return proxied.apply(this, arguments);
                };
            }
        } catch (e) {
            // if anything fails, gtm proceeds as normal
            return proxied.apply(this, arguments);
          }
	}

        //HELPERS
        //helper to find substrings in blacklist
        function doesContain(blackList, term) {
            if (blackList.some(function(v) {
                    return term.indexOf(v) >= 0
                })) {
                return true
            } else {
                return false
            }
        }

        //helper to find the intersection of two arrays
        function unionArr(array1, array2) {
            return array1.filter(function(n) {
                return array2.indexOf(n) !== -1;
            });
        }

        //helper to clean dataLayer objects into a flat structure
        function cleanObject(object) {
            var simpleObject = {};
            for (var prop in object) {
                if (!object.hasOwnProperty(prop)) {
                    continue;
                }
                if (typeof(object[prop]) == 'object') {
                    continue;
                }
                if (typeof(object[prop]) == 'function') {
                    continue;
                }
                simpleObject[prop] = object[prop];
            }
            return simpleObject; // returns cleaned up JSON
        }    
})();
</script>

for firing triggers on this tag, ensure this code runs after our first (init) tag:


(this is important because this snippet depends upon the mixpanel library; so the sequencing matters)

Step 4: Deploy!

deploy these two new tags and you should instantly start seeing mixpanel events in your project, under the events tab.

once you do have data flowing, for further governance (renaming stuff, altering the event schema) check out mixpanel's data administration tool, lexicon

Step 5: (optional) Configuration

feel free to read the "patch" comments; there are some governance features that are worth highlighting:

which events get tracked by name:
//events that CONTAIN the following names WILL NOT be tracked
var eventBlackList = ['gtm.init', 'gtm.dom', 'gtm.load', 'gtm.scrollDepth', 'gtm.click', 'optimize.']
which events get tracked by property keys:
//events that HAVE the following properties KEYS WILL NOT be tracked
var propertyKeyBlackList = ['foo', 'bar', 'baz']
which events get tracked by property values:
//events that HAVE the following properties VALUES WILL NOT be tracked
var propertyValueBlackList = ['qux', 'mux', 'dux']

in layman's terms:

this code "listens" to the GTM dataLayer and will automatically forward events to mixpanel unless:

  • the event name contains one of the items on the blacklist
  • a property/dimension on the event has a key on the blacklist
  • a property/dimension on the event has a value on the blacklist

note: you may not wish to use all of these governance features (like ignore events by property key/value). that's fine, just set the value of those variables to an empty array []

final notes:

there's a great browser extension: Tag Manager Assistant made by google that can help you to debug your current tag setup... it lets you peel back all the "data layers" of any GTM implementation, and you can use it on your production website.

before you muck around with filtering and governance features, it's a good idea to familiarize yourself with the tags you already have deployed:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment