Skip to main content

Integrating Sidecar with GTM for tracking

info

This integration is for tracking purposes only. We strongly advise against loading Sidecar itself via GTM, as this will delay the changes from being applied and result in "flickering".

Overview

This integration with GTM will automatically send any GTM-tagged events to Statsig.
No additional coding or tagging is required after completing these steps.

image (statsig logstream showing gtm events flowing in)

Step 1: Create new tag

image

Step 2: Choose tag type

Choose "Custom HTML" for tag type, and paste this GTM code (including script tag)

image

Step 3: Adjust fire options

Under Advanced Settings under "Tag Firing options", select "Once per page"

image

Step 4: Set Tag Trigger

Below the "Tag Configuration" section, set the Trigger to "Initialization - All Pages" Option.

image

Step 5: Save tag and test

After saving the tag, and publishing your updated GTM tag, tracking will be done automatically without any additional configuration. To debug the integration, you can set a local storage entry debug_ss_gtm with any value on your webpage. Now, you'll console log statements for each tracking call being dispatched to Statsig. You can also inspect your browser's network traffic to see events being tracked.

GTM Code

<script type="text/javascript">
/* dataLayer helper */
(function(){
var f=/\[object (Boolean|Number|String|Function|Array|Date|RegExp|Arguments)\]/;function g(a){return null==a?String(a):(a=f.exec(Object.prototype.toString.call(Object(a))))?a[1].toLowerCase():"object"}function m(a,b){return Object.prototype.hasOwnProperty.call(Object(a),b)}function n(a){if(!a||"object"!=g(a)||a.nodeType||a==a.window)return!1;try{if(a.constructor&&!m(a,"constructor")&&!m(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}for(var b in a);return void 0===b||m(a,b)};function p(a,b){var c={},d=c;a=a.split(".");for(var e=0;e<a.length-1;e++)d=d[a[e]]={};d[a[a.length-1]]=b;return c}function q(a,b){var c=!a._clear,d;for(d in a)if(m(a,d)){var e=a[d];"array"===g(e)&&c?("array"===g(b[d])||(b[d]=[]),q(e,b[d])):n(e)&&c?(n(b[d])||(b[d]={}),q(e,b[d])):b[d]=e}delete b._clear};
function r(a,b,c){b=void 0===b?{}:b;"function"===typeof b?b={listener:b,listenToPast:void 0===c?!1:c,processNow:!0,commandProcessors:{}}:b={listener:b.listener||function(){},listenToPast:b.listenToPast||!1,processNow:void 0===b.processNow?!0:b.processNow,commandProcessors:b.commandProcessors||{}};this.a=a;this.l=b.listener;this.j=b.listenToPast;this.g=this.i=!1;this.c={};this.f=[];this.b=b.commandProcessors;this.h=u(this);var d=this.a.push,e=this;this.a.push=function(){var k=[].slice.call(arguments,
0),l=d.apply(e.a,k);v(e,k);return l};b.processNow&&this.process()}r.prototype.process=function(){this.registerProcessor("set",function(){var c={};1===arguments.length&&"object"===g(arguments[0])?c=arguments[0]:2===arguments.length&&"string"===g(arguments[0])&&(c=p(arguments[0],arguments[1]));return c});this.i=!0;for(var a=this.a.length,b=0;b<a;b++)v(this,[this.a[b]],!this.j)};r.prototype.get=function(a){var b=this.c;a=a.split(".");for(var c=0;c<a.length;c++){if(void 0===b[a[c]])return;b=b[a[c]]}return b};
r.prototype.flatten=function(){this.a.splice(0,this.a.length);this.a[0]={};q(this.c,this.a[0])};r.prototype.registerProcessor=function(a,b){a in this.b||(this.b[a]=[]);this.b[a].push(b)};
function v(a,b,c){c=void 0===c?!1:c;if(a.i&&(a.f.push.apply(a.f,b),!a.g))for(;0<a.f.length;){b=a.f.shift();if("array"===g(b))a:{var d=a.c;g(b[0]);for(var e=b[0].split("."),k=e.pop(),l=b.slice(1),h=0;h<e.length;h++){if(void 0===d[e[h]])break a;d=d[e[h]]}try{d[k].apply(d,l)}catch(w){}}else if("arguments"===g(b)){e=a;k=[];l=b[0];if(e.b[l])for(d=e.b[l].length,h=0;h<d;h++)k.push(e.b[l][h].apply(e.h,[].slice.call(b,1)));a.f.push.apply(a.f,k)}else if("function"==typeof b)try{b.call(a.h)}catch(w){}else if(n(b))for(var t in b)q(p(t,
b[t]),a.c);else continue;c||(a.g=!0,a.l(a.c,b),a.g=!1)}}r.prototype.registerProcessor=r.prototype.registerProcessor;r.prototype.flatten=r.prototype.flatten;r.prototype.get=r.prototype.get;r.prototype.process=r.prototype.process;window.DataLayerHelper=r;function u(a){return{set:function(b,c){q(p(b,c),a.c)},get:function(b){return a.get(b)}}};})();

window.StatsigLogger = (function () {

var statsigInstance, log = function () {
if (typeof localStorage !== 'undefined' && typeof console !== 'undefined' && localStorage.getItem('debug_ss_gtm')) {
console.log.apply(console, arguments);
}
};

var checkStatsigReady = function (callback) {
var checkerLoop, idx = 1;
var killLoop = function () {
log('+++ Stop checkStatsigReady after ' + idx + ' checks');
try {
clearTimeout(checkerLoop);
} catch (err) { }
};
(function checkerFcn() {
if (idx >= 10) {
killLoop();
return;
}
try {
log('+++ Check num: ' + idx);
if (StatsigSidecar.getStatsigInstance().ready) {
log('+++ Statsig Ready!');
killLoop();
callback();
return;
}
} catch (err) { }
idx++;
checkerLoop = setTimeout(checkerFcn, 250);
})();
};

var handleGTMMessage = function (model, message) {
if (typeof message === 'object' && typeof message.event === 'string') {
var metadata = {};
for (var prop in message) {
if (prop.includes('gtm.') && !!message[prop] && (typeof message[prop] === 'number' || typeof message[prop] === 'boolean' || typeof message[prop] === 'string')) {
metadata[prop] = message[prop];
}
}
log('++ handleGTMMessage', message.event, message.conversionValue || null, metadata);
statsigInstance.logEvent(message.event, message.conversionValue || null, metadata);
}
else {
log('++ handleGTMMessage / skip');
}
}

checkStatsigReady(function () {
log('+++ Statsig:ready');
statsigInstance = StatsigSidecar.getStatsigInstance();
// dataLayer will contain gtm events (~messages) that were fired pre statsig readiness
new DataLayerHelper(dataLayer, {
listener: handleGTMMessage,
listenToPast: true,
});
});

})();
</script>