Integrating Sidecar with GTM for tracking
Set up Google Tag Manager integration with Sidecar to automatically send GTM-tagged events to Statsig for experiment tracking.
This integration is for tracking purposes only. Avoid loading Sidecar itself through GTM, as this delays the changes from being applied and results in "flickering".
How GTM integration works
This integration automatically sends GTM-tagged events to Statsig. No additional coding or tagging is required after completing these steps.

(statsig logstream showing gtm events flowing in)
Step 1: Create new tag

Step 2: Choose tag type
Choose "Custom HTML" for tag type, and paste this GTM code (including script tag)
Step 3: Adjust fire options
Under Advanced Settings under "Tag Firing options", select "Once per page"

Step 4: Set tag trigger
Below the "Tag Configuration" section, set the Trigger to "Initialization - All Pages" Option.

Step 5: Save tag and test
After saving and publishing your updated GTM tag, tracking runs automatically without any additional configuration. To debug the integration, set a local storage entry debug_ss_gtm with any value on your webpage. This produces console log statements for each tracking call 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) {
// statsig has already loaded
if(typeof StatsigSidecar !== 'undefined' && StatsigSidecar.getStatsigInstance()) {
log('+++ Statsig:ready Already loaded');
callback(StatsigSidecar.getStatsigInstance());
}
else {
log('+++ Statsig:ready callback');
window.postExperimentCallback = function(statsigClient, expIds) {
callback(statsigClient);
}
}
};
var handleGTMMessage = function (model, message) {
if (typeof message === 'object' && typeof message.event === 'string') {
var metadata = {};
for (var prop in message) {
if(!(message[prop] instanceof HTMLElement) && typeof(message[prop]) !== 'object') {
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 (statsigInstanceReady) {
statsigInstance = statsigInstanceReady;
// dataLayer will contain gtm events (~messages) that were fired pre statsig readiness
new DataLayerHelper(dataLayer, {
listener: handleGTMMessage,
listenToPast: true,
});
});
return {
track: function() {
try {
statsigInstance.logEvent.apply(statsigInstance, arguments);
} catch(err) { }
}
}
})();
</script>
Was this helpful?