This article will help you:
  • Integrate Optimizely and Google Universal Analytics through Google Tag Manger 
  • Integrate Optimizely if you have pre-existing Universal Analytics tags
  • Confirm your integration

There are a few different implementation configurations available when integrating Optimizely Classic with Google Universal Analytics through Google Tag Manager.

We don't recommend that you load the Optimizely snippet through a tag manager. 

Loading Optimizely through a tag manager can cause issues such as page flashing, where the original version of your page is briefly displayed to visitors before the Optimizely experiment. Google Tag Manager, for example, doesn't support synchronous loading. Tag managers can also cause issues with your analytics integrations. To deliver the best experience to your visitors, implement Optimizely outside of a tag manager.

If you must load Optimizely through a tag manager, first consider these pitfalls and implementation suggestions and please reach out to your Customer Success Manager to discuss the implications.

Setting up Google Analytics Custom Dimension and Integration

Before selecting the appropriate implementation you'll need set up a Google Analytics custom dimension and custom report and enable the integration within Optimizely.

In Google Analytics:

  • Create a Custom Dimension that will receive Optimizely experiment information. In order to integrate Optimizely with Universal Analytics and view reports based on the integration data, you must configure a Universal Analytics custom dimension first. This dimension, and the name you give it, will be used to create reports that leverage your Optimizely integration data for filtering.

In Optimizely:

  • Under the Integrations tab in the Home page, turn on the Google Universal Analytics integration.

  • Load the experiment in the visual editor navigate to Options --> Integrations. Enable the Google Universal Analytics integration for this experiment. Enter in the index of the custom dimension you created in Google Analytics.

  • If you are using a custom tracker add the tracker name and make sure the name in the field matches.                                 

Setting up Custom Report in Google Analytics

You'll want to see reports on your experiments within Google UA, so you'll need a custom report for each experiment. Follow the instructions in this article on how to create a custom report.

New implementations: no pre-existing analytics integration

With this implementation, the Optimizely snippet has been hard-coded under the <head> tag and the GTM container script is added to the <body> tag. This configuration ensures that Optimizely is fully loaded before it implements code in the GTM container and allows Optimizely to execute synchronously and pass the necessary information between Optimizely and Google Analytics.

If you’re an existing Google Tag Manager user, you may already have pre-configured Universal Analytics tags that fire off pageview calls. If this is the case, then skip to the section below on Google Tag Manager and Universal Analytics.

Load Optimizely synchronously outside of Google Tag Manager

This approach is the most likely to prevent "flickering" or "flashing," as GTM does not support synchronous loading. Let's walk through the process:

  1. Make sure that the Optimizely snippet is being loaded in the <head> tag of your page, outside of GTM.

  2. Add a Custom HTML tag in Google Tag Manager. For Tag Type, select Custom HTML Tagnot Google Analytics Tag.

  3. Copy and paste your Google Universal Analytics tracking code in the HTML box under Configure Tag. The tracking code can be obtained from your Google Analytics account by navigating to the Admin tab, then clicking Tracking Info > Tracking Code.

  4.  Add the Optimizely activation call between the 'create' and 'send' calls: 

          // Optimizely Universal Analytics Integration Code
          window.optimizely = window.optimizely || [];

    Here is an example:

    //Universal Analytics Setup
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
          ga('create', 'UA-XXXX-Y', 'auto');
          // Optimizely Universal Analytics Integration Code
          window.optimizely = window.optimizely || [];
    // End Optimizely Code
          ga('send', 'pageview');
  5. Make sure 'UA-XXXX-Y' is replaced with with your Google Analytics tracking ID.

Here's an example of what the above code might look like in GTM:

  1. Make sure that your tag has a firing rule set. Choose the "Page View" Event type. 

  2. Back in your site's code, make sure that the GTM container script is added in the <body> tag of your site. 

Why did we use the Custom HTML tag instead of the Google Analytics tag option? Usually, Custom HTML can be used to include any tag that doesn’t make visible changes to the page and doesn’t need to be fired synchronously or in a specific order. However, in this case, we needed to use it to add the Optimizely activation call into the normal Google Analytics snippet.

Existing implementations using the Universal Analytics pageview tag: update the custom HTML tag in GTM

If you’re an existing Google Tag Manager user, you may already have pre-configured Universal Analytics tags that fire off pageview calls, which look like this in GTM:

If you do not already have pre-existing Universal Analytics pageview tags configured in your GTM account, follow the instructions in New implementations: no pre-existing analytics integration.

If this is the case, then:

  • It’s not ideal to create the custom HTML tag to simply add the Optimizely integration, especially when you are using multiple “Advanced settings” in the tags.

  • Having the custom HTML tag fire in conjunction with these existing template tags would end up in possibly skewed results due to UA making multiple pageview calls.

In this case, it makes more sense to update the existing tags so that you can still filter your results with Optimizely segments. 

Integrate with Universal Analytics in Google Tag Manager using GTM’s custom JavaScript Macro

As is the case with all asynchronously loaded experiments (Conditionally activated, geo-targeted, etc. - anything where the timing of when the experiment is defined on the page may be affected), this solution will need to be adjusted to account for timing issues these sort of experiments present. 


In Google Tag Manager:

  1. Create a new User-Defined variable within Google Tag Manager (you can find this option under Container > Variables). The Type should be a Custom JavaScript Variable

  2. Here, we will use slot 3 as an example - this corresponds to Custom Dimension 3 as outlined in the GA and Optimizely examples above. You can replace this number with whichever index you select in your GA interface. For the variable name, use "Optimizely Variable Slot 3" and put this code (below the screenshot) within the Custom JavaScript section, then save it: 

    function () {
          var o = window.optimizely,
              aE = o.activeExperiments,
              v =,
              gaKey = "",
              gaValue = "",
              rD =;
          for(var i = 0; i < aE.length; i++) {
             var e = aE[i],
             vId = o.variationIdsMap[e],
             vIndex = o.variationMap[e],
             isMvt = vId.length > 1,
             eName =[e].name;
             gaKey = (isMvt ? "MVT" : "AB") + " Test: " + eName + "";
             if (!isMvt) {
                 gaValue = (vIndex === 0 ? "Control" : "V") + vIndex + ": " + o.variationNamesMap[e];
             } else {
                 var s =,
                     sId =[e].section_ids;
                 for (i = 0; i < sId.length; i++) {
                    if(i == 0) {
                      gaValue = s[sId[i]].name.toString() + ': ' + v[vId[i]].name;
                    } else {
                      gaValue += ', ' + s[sId[i]].name.toString() + ': ' + v[vId[i]].name;
             if (gaKey.length !== 0 && gaValue.length !== 0 && o.allExperiments[e].universal_analytics && o.allExperiments[e].universal_analytics.slot == 3) {
               return gaKey+": "+gaValue;
         if (rD !== undefined) {
             var rDe = rD.experimentId,
                 rvId = o.variationIdsMap[rDe],
                 rVIndex = o.variationMap[rDe],
                 rIsMvt = rvId.length > 1,
                 rEName =[rDe].name,
                 rGaKey = "",
                 rGaValue = "";
             rGaKey = (rIsMvt ? "MVT Redirect" : "AB Redirect") + " Test: " + rEName + "";
             if (!rIsMvt) {
                 rGaValue = (rVIndex === 0 ? "Control" : "V") + rVIndex + " Redirect: " + o.variationNamesMap[rDe];
             } else {
                 var sR =,
                     sIdR =[rDe].section_ids;
                for(i = 0; i < sIdR.length; i++) {
                     rGaValueArray = sR[sIdR[i]].name.toString() + "Redirect: " + v[rvId[i]].name;
             if (rGaKey.length !== 0 && rGaValue.length !== 0 && o.allExperiments[rDe].universal_analytics && o.allExperiments[rDe].universal_analytics.slot == 3) {
               return rGaKey+": "+rGaValue;

Modify the sample JavaScript above by replacing custom dimension slot on lines 30 and 55 with the index of the GA custom dimension you created.

  1. This code above will return the experiment name and variation name key/value pair within Google Tag Manager.

  2. Within an existing pageview tag in Google Tag Manager, navigate to More Settings > Custom Dimensions. Specify the open slot that you'd like to use for your Optimizely experiment. In the Dimension field, choose Optimizely Variable Slot 3Google Tag Manager Custom Javascript Variable

  3. Under Configure Tag --> More Settings --> Advanced Configuration --> Check the "Tracker name" box and leave the name field blank. 

If you're specifying a tracker name, make sure the name in the field matches.

  1. Finally, in the custom report within Google Analytics for slot 3, don’t filter by anything specific; just use a wildcard RegEx (regular expression) match .*.

The above guide assumes that you've already installed Optimizely natively in the <head> tag, and it’s firing synchronously. Also, here we choose slot 3 as an example. If you'd like to use other UA slots, change the naming and most importantly change the slot number within the "if" logic of the code provided as well as your Custom Dimension and report definitions.

Other Google Tag Manager implementations

Use Google Tag Manager to deploy both Optimizely snippet and your analytics code

This is not a recommended solution, but if you must load both Optimizely and your analytics code within Google Tag Manager, here is a workaround that will allow you to do that. The key is that you want to force Optimizely to activate before your analytics code, otherwise the integration will not work.

  1. First, deploy the Optimizely tag: Add the Optimizely snippet to a Custom HTML tag in Google Tag Manager. Google provides a warning that custom tags should not be used for A/B testing, but if you're going to implement Optimizely from within Google Tag Manager, this is the only way to proceed.

  2. In the same tag, after the snippet, add a dataLayer variable that executes your analytics platform. Your tag in Google Tag Manager should look something like this: 

  3. Still in Google Tag Manager, create a new rule called something like "All pages after Optimizely"

  4. Specify the following conditions with Event type "Custom Event":
    url matches Regex .*
    event equals optimizely_loaded

  5. Here's what your Optimizely tag should look like in GTM. Set the firing priority to 2

  6. And here's what your GA tag should look like, including firing rules that ensure that GA deploys after Optimizely. Set the firing priority to 1:

The solution above uses Custom HTML tags to deploy both Optimizely and Google Analytics. If you're using template tags (like 'pageview' tags) to deploy GA Classic, you'll need to follow a few extra steps.

  1. Go to your Google Analytics tag in Google Tag Manager, and navigate to More Settings > Advanced Configuration > Tracker Name.

  2. Check the Tracker Name box, but leave the text box blank.

  3. Click Save. This will allow you to pass Optimizely custom variables to your analytics platform.

  4. Please note that this solution will not currently work for Universal Analytics.

For other examples of this integration, check out these excellent walkthroughs by Tyson Kirksey at Vertical Nerve (who also owns the two above images) and John Pash at Easyart.

Using this method, we are able to ensure that Optimizely loads completely before UA fires in GTM, but you may still experience "flashing" or "flickering" because this setup uses asynchronous deployment. 

Deploy Optimizely through GTM but hard-code your analytics code on your site

Unfortunately, this setup will not work due to the asynchronous nature of the GTM container. In this setup, we cannot specify the order of execution of the Optimizely tag in relation to the hard-coded analytics tag; we can only specify its firing/loading completion priority in relation to other tags deployed in GTM. This will likely cause errors with your Optimizely/analytics integrations and page flashing/flickering.

Use GTM’s firing priority feature to specify the order of execution

As of July 1st, 2014, Google added a Tag Firing Priority option that allows "tags with higher numbers for priority [to] be fired first. Priority defaults to 0 if none is specified."

The Tag Firing Priority feature is intended for sites that have many tags and third-party scripts that use the same firing rule (for example, regex equals .*)

Unfortunately, this will not work with Optimizely because even though tag priority is set, the solution is still asynchronous, and tags will fire regardless of whether the previous tag has finished firing. In other words, this feature guarantees that Optimizely will fire in a certain order, but not load in a certain order. In the case of Optimizely and UA, Optimizely will not have finished processing before UA fires. This breaks the Optimizely/UA integration because custom dimensions will not be set properly.

Confirm your integration

Google UA's custom dimensions contain visitor-specific information that can be sent along with events and pageviews. It can take up to 24 hours to populate that data into Google UA, but the custom dimension is set immediately when you integrate Google UA and Optimizely. This means you can confirm that the custom dimension is set correctly with the Google Analytics’ debugger tool.

Using the debugger tool, look for the variable cd[x] where x is the slot that corresponds to the Optimizely experiment. The variable is set with the Optimizely experiment, followed by the variation you were bucketed into. Here's an example:

dimension3 (&cd3) Optimizely_Onboarding (1645002506): Variant#1

Optimizely and Google Analytics capture and report data differently and will not always align. However, large discrepancies can happen for various reason. This article will provides details on how Optimizely and Google Analytics record visitor's conversions and how to debug large discrepanices. 

Preserving referral sources during redirects

When a redirect takes place in Optimizely with the UA integration enabled, we currently grab the document.referrer value and call the ga('set','referrer'); function in order to maintain the original referrer. This works great on landing pages; however if there is a redirect on any page deeper into your site this strips the visitor's original session referrer and makes it the immediately preceding page. This will inflate your UA and AdWords reports' Referral traffic source.

Implement this code linked on Github so that it runs on every page load above the Optimizely snippet, or in Project JavaScript. On the initial landing page, this code will grab the session's initial referrer and set it to a cookie. This cookie persists through the user's navigation on your site. When the user is redirected on a non-landing page as part of an Optimizely experiment, this code will determine if the existing session's referrer should be preserved, or if it should be updated to document.referrer. Then the code sends the appropriate referrer to UA. As a result, the original traffic source is persistent.

Please be sure to note the implementation instructions at the top of the code.