Non-flickering Experiments

This article will help you understand

  • What is the context of this option being provided in addition to the regular Experiments
  • How non-flickering experiments work and why are they useful
  • What are the limitations and also workarounds of this solution
  • How to integrate the solution into Exponea

Context and information

The original Experiments solution has some limitations. While working with experiments, a change made by the user sometimes comes with a delay of a couple of seconds before the change is actually displayed on the website. The visual change, therefore, happens additionally, usually also unexpectedly and non-conveniently for the user. This is the "flickering effect" that is to be avoided by this solution.

Due to this issue, this solution consisting of an entirely new way of loading and applying of experiments has been designed. With non-flickering experiments, alterations of the website and its design will be displayed instantly in real-time, even if the webiste is being dynamically changed.

Both of these approaches (the original one as well as this additional one) have their limitations, therefore select a solution you prefer and which has the advantages you find more beneficial.

How do non-flickering experiments work and why are they useful?

In the original Experiments, after opening the website and subsequently activating the status "document ready", all of the modifications were applied at once. This creates two problems.

a) The user, although already on the website, is not seeing the changes because the "document ready" status may not be active yet. Therefore, the already mentioned "flickering effect" is happening.
b) Modifications are applied only once. The ones for which the suitable elements on the page were found, were applied in the given order and that has ended the experiment.

The new version initializes before any content of the website arrives and is able to see every change on the website. As the new elements are uploaded on the website, they are instantly reviewed whether they comply with the modification selectors and if yes, the modification is instantly applied.
Due to this method, the user will see the changes right away.

Furthermore, this is happening constantly, therefore also a dynamically generated content of the website (for instance a Single Page App or a modal window) is included and being reviewed. As a result, the modifications may not happen in a strict order.

Limitations and workarounds

This new solution is faster in all aspects in comparison to the original Experiments. However, some factors described below may cause a decrease in the speed of this new solution. It is beneficial to consider these factors and act accordingly, even though the non-flickering experiments will still be faster than the original solution.

Usage of the customer filter in the experiment

In the experiment settings (as shown in the picture below), the application of certain customer filters in the experiment may cause issues in the speed of the application of the experiment modifications.

This is because if the filter is used, the experiment will arrive at the web browser only in the second asynchronous request as opposed to the first one, which may be too late for the non-flickering experiments. This limitation applies to the experiment as a whole.

In particular, the issue may occur when

  • too many aggregates are being used
  • events are being used to capture the whole history of your customers

You can easily solve this problem by specifying the date filter so that the software does not have to compute the history of your customers´ events.

Usage of Jinja in the experiment

Every experiment consists either of one or more modifications. These modifications can be fast (non-flickering) and slow - the ones using Jinja. If the modifications include Jinja, they are marked as "slow" by this specific icon.

These marked modifications (the ones including Jinja) will get to the browser in the second request, because their processing may take more time. All of the other fast non-flickering modifications above the first slow one will return already in the first request.

If you have a fast non-flickering modification under the slow one with Jinja, an orange icon with an exclamation point will show up, indicating that moving the fast modification above the slow one is recommended, of course only if it doesn't affect the final result.

However, if you need to use Jinja in a non-flickering experiment and it causes a delay you want to remove, there are two possible workarounds.

Workaround 1

If the retrieving of the content for the website takes longer because of Jinja, the first workaround consisting of the technique of placeholders is to be used.

Add the future content that known in advance (or at least containers for the future content) into the website beforehand and the modification with the desired content that is loaded in a more slowly fashion will be added afterwards. Therefore, the space for this new content brought in by the modification will be created in advance in the layout of the website (quickly - non-flickering). As a result, the content added additionally will not create an unstable impression of layout changes on the go. Moreover, we can also smoothly animate the content (fade-in) to improve the customer experience even further. Nowadays, customers are quite used to placeholders and they subconsciously expect a portion of content to be loaded additionally if there is some blank space for a split-second.

In the picture below you may see an illustration of this workaround. The first non-flickering modification adds 10 placeholders into the website and the second slow modification (half a second later) with the help of Jinja finishes the loading of the products for the specific customer.

Workaround 2

The second workaround uses "on the next page view". It works by saving the result of the slow modification (with Jinja) into localStorage. During the display of the following page, it will read the data and apply it using the non-flickering technique.

The negative aspect of this workaround is that you will not be able to apply the experiment on the first page-view (unless you create a hybrid fix using also the first workaround). In other words, the page will show the content based on the information that is gained one page-view behind. However, we will still have the non-flickering solution and taking into account our experience with these issues, this negative aspect is acceptable in the majority of use cases.


As non-flickering experiments work in a different way compared to the original experiments, some of the already finished experiments may not work correctly with the new solution. Therefore, it is necessary to test all of the active experiments after applying this new solution.

Integration into Exponea



We are currently working on a new integration snippet with built-in support for non-flickering experiments. This documentation section will be updated once this is done, very soon. In the meantime, it is not recommended to use the snippet in this section because it can block the rendering of your website. If there's an error on our side, the script blocks the rendering of the page for a long time. We are working on a better solution to this.

All you need to do for non-flickering experiments is to replace the old (current) Exponea integration snippet on your page with the new non-flickering snippet which you can copy below.

The Exponea snippet is inserted into the <head> of your HTML code as explained in the Integrating Exponea (developers) article. If you don't insert the integration snippet into the <head>, non-flickering experiments won't work properly. Preferably, insert the snippet right under the <title> tag before all of the other stylesheets and scripts.

The new snippet will hold up the loading of the website by 50ms on average regardless of whether it will be at the beginning of the header or not, but we will gain time for calling the asynchronous demand for the slower experiment modifications, which however do not hold up the loading of the website. The maximum time of this website loading delay is set to 200ms.

This delay is essentially the only disadvantage compared to the original solution.

<script>!function(){"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,n){return t(n={exports:{}},n.exports),n.exports}function e(t){return t&&t.Math==Math&&t}function r(t){try{return!!t()}catch(t){return!0}}function o(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}function i(t){return,-1)}function f(t){if(null==t)throw TypeError("Can't call method on "+t);return t}function a(t){return j(f(t))}function l(t){return"object"==typeof t?null!==t:"function"==typeof t}function c(t,n){if(!l(t))return t;var e,r;if(n&&"function"==typeof(e=t.toString)&&!l( r;if("function"==typeof(e=t.valueOf)&&!l( r;if(!n&&"function"==typeof(e=t.toString)&&!l( r;throw TypeError("Can't convert object to primitive value")}function s(t,n){return,n)}function u(t){if(!l(t))throw TypeError(String(t)+" is not an object");return t}function p(n,e){try{z(b,n,e)}catch(t){b[n]=e}return e}function y(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++B+W).toString(36)}var d,g,v,h,m="object",b=e(typeof globalThis==m&&globalThis)||e(typeof window==m&&window)||e(typeof self==m&&self)||e(typeof t==m&&t)||Function("return this")(),w=!r(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}),_={}.propertyIsEnumerable,x=Object.getOwnPropertyDescriptor,O={f:x&&!{1:2},1)?function(t){var n=x(this,t);return!!n&&n.enumerable}:_},S={}.toString,E="".split,j=r(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==i(t)?,""):Object(t)}:Object,T={}.hasOwnProperty,P=b.document,k=l(P)&&l(P.createElement),A=!w&&!r(function(){return 7!=Object.defineProperty(function(t){return k?P.createElement(t):{}}("div"),"a",{get:function(){return 7}}).a}),M=Object.getOwnPropertyDescriptor,R={f:w?M:function(t,n){if(t=a(t),n=c(n,!0),A)try{return M(t,n)}catch(t){}if(s(t,n))return o(!,n),t[n])}},C=Object.defineProperty,I={f:w?C:function(t,n,e){if(u(t),n=c(n,!0),u(e),A)try{return C(t,n,e)}catch(t){}if("get"in e||"set"in e)throw TypeError("Accessors not supported");return"value"in e&&(t[n]=e.value),t}},z=w?function(t,n,e){return I.f(t,n,o(1,e))}:function(t,n,e){return t[n]=e,t},L=n(function(t){var n="__core-js_shared__",e=b[n]||p(n,{});(t.exports=function(t,n){return e[t]||(e[t]=void 0!==n?n:{})})("versions",[]).push({version:"3.2.1",mode:"global",copyright:"© 2019 Denis Pushkarev ("})}),N=L("native-function-to-string",Function.toString),D=b.WeakMap,F="function"==typeof D&&/native code/.test(,B=0,W=Math.random(),G=L("keys"),H={},q=b.WeakMap;if(F){var K=new q,V=K.get,Y=K.has,J=K.set;d=function(t,n){return,t,n),n},g=function(t){return,t)||{}},v=function(t){return,t)}}else{var Q=G[h="state"]||(G[h]=y(h));H[Q]=!0,d=function(t,n){return z(t,Q,n),n},g=function(t){return s(t,Q)?t[Q]:{}},v=function(t){return s(t,Q)}}function U(t){return"function"==typeof t?t:void 0}function X(t,n){return arguments.length<2?U(ft[t])||U(b[t]):ft[t]&&ft[t][n]||b[t]&&b[t][n]}function Z(t){return isNaN(t=+t)?0:(0<t?st:lt)(t)}function $(t){return 0<t?pt(Z(t),9007199254740991):0}function tt(u){return function(t,n,e){var r,o=a(t),i=$(o.length),c=function(t,n){var e=Z(t);return e<0?yt(e+n,0):dt(e,n)}(e,i);if(u&&n!=n){for(;c<i;)if((r=o[c++])!=r)return!0}else for(;c<i;c++)if((u||c in o)&&o[c]===n)return u||c||0;return!u&&-1}}function nt(t,n){for(var e=bt(n),r=I.f,o=R.f,i=0;i<e.length;i++){var c=e[i];s(t,c)||r(t,c,o(n,c))}}function et(t,n){var e=xt[_t(t)];return e==St||e!=Ot&&("function"==typeof n?r(n):!!n)}function rt(t,n,e){var r=c(n);r in t?I.f(t,r,o(0,e)):t[r]=e}function ot(t){return At[t]||(At[t]=Pt&&kt[t]||(Pt?kt:y)("Symbol."+t))}function it(t){if(!l(t))return!1;var n=t[Ct];return void 0!==n?!!n:Tt(t)}var ct,ut={set:d,get:g,has:v,enforce:function(t){return v(t)?g(t):d(t,{})},getterFor:function(e){return function(t){var n;if(!l(t)||(n=g(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}},at=n(function(t){var n=ut.get,u=ut.enforce,a=String(N).split("toString");L("inspectSource",function(t){return}),(t.exports=function(t,n,e,r){var o=!!r&&!!r.unsafe,i=!!r&&!!r.enumerable,c=!!r&&!!r.noTargetGet;"function"==typeof e&&("string"!=typeof n||s(e,"name")||z(e,"name",n),u(e).source=a.join("string"==typeof n?n:"")),t!==b?(o?!c&&t[n]&&(i=!0):delete t[n],i?t[n]=e:z(t,n,e)):i?t[n]=e:p(n,e)})(Function.prototype,"toString",function(){return"function"==typeof this&&n(this).source||})}),ft=b,lt=Math.ceil,st=Math.floor,pt=Math.min,yt=Math.max,dt=Math.min,gt={includes:tt(!0),indexOf:tt(!1)}.indexOf,vt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"].concat("length","prototype"),ht={f:Object.getOwnPropertyNames||function(t){return function(t,n){var e,r=a(t),o=0,i=[];for(e in r)!s(H,e)&&s(r,e)&&i.push(e);for(;n.length>o;)s(r,e=n[o++])&&(~gt(i,e)||i.push(e));return i}(t,vt)}},mt={f:Object.getOwnPropertySymbols},bt=X("Reflect","ownKeys")||function(t){var n=ht.f(u(t)),e=mt.f;return e?n.concat(e(t)):n},wt=/#|\.prototype\./,_t=et.normalize=function(t){return String(t).replace(wt,".").toLowerCase()},{},Ot=et.NATIVE="N",St=et.POLYFILL="P",Et=et,jt=R.f,Tt=Array.isArray||function(t){return"Array"==i(t)},Pt=!!Object.getOwnPropertySymbols&&!r(function(){return!String(Symbol())}),kt=b.Symbol,At=L("wks"),Mt=ot("species"),Rt=ot("species"),Ct=ot("isConcatSpreadable"),It=9007199254740991,zt="Maximum allowed index exceeded",Lt=!r(function(){var t=[];return t[Ct]=!1,t.concat()[0]!==t}),Nt=(ct="concat",!r(function(){var t=[];return(t.constructor={})[Rt]=function(){return{foo:1}},1!==t[ct](Boolean).foo}));!function(t,n){var e,r,o,i,c,,,f=t.stat;if(e=a?b:f?b[u]||p(u,{}):(b[u]||{}).prototype)for(r in n){if(i=n[r],o=t.noTargetGet?(c=jt(e,r))&&c.value:e[r],!Et(a?r:u+(f?".":"#")+r,t.forced)&&void 0!==o){if(typeof i==typeof o)continue;nt(i,o)}(t.sham||o&&o.sham)&&z(i,"sham",!0),at(e,r,i,t)}}({target:"Array",proto:!0,forced:!Lt||!Nt},{concat:function(t){var n,e,r,o,i,c=function(t){return Object(f(t))}(this),u=function(t,n){var e;return Tt(t)&&("function"!=typeof(e=t.constructor)||e!==Array&&!Tt(e.prototype)?l(e)&&null===(e=e[Mt])&&(e=void 0):e=void 0),new(void 0===e?Array:e)(0===n?0:n)}(c,0),a=0;for(n=-1,r=arguments.length;n<r;n++)if(it(i=-1===n?c:arguments[n])){if(o=$(i.length),It<a+o)throw TypeError(zt);for(e=0;e<o;e++,a++)e in i&&rt(u,a,i[e])}else{if(It<=a)throw TypeError(zt);rt(u,a++,i)}return u.length=a,u}});function Dt(){var t=u(this),n="";return"g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.dotAll&&(n+="s"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}var Ft,Bt,Wt=Object.setPrototypeOf||("__proto__"in{}?function(){var e,r=!1,t={};try{(e=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(t,[]),r=t instanceof Array}catch(t){}return function(t,n){return u(t),function(t){if(!l(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype")}(n),r?,n):t.__proto__=n,t}}():void 0),Gt=ot("match"),Ht=ot("species"),qt=I.f,Kt=ht.f,Vt=ot("match"),Yt=b.RegExp,Jt=Yt.prototype,Qt=/a/g,Ut=/a/g,Xt=new Yt(Qt)!==Qt;if(w&&Et("RegExp",!Xt||r(function(){return Ut[Vt]=!1,Yt(Qt)!=Qt||Yt(Ut)==Ut||"/a/i"!=Yt(Qt,"i")}))){for(var Zt=function(t,n){var e=this instanceof Zt,r=function(t){var n;return l(t)&&(void 0!==(n=t[Gt])?!!n:"RegExp"==i(t))}(t),o=void 0===n;return!e&&r&&t.constructor===Zt&&o?t:function(t,n,e){var r,o;return Wt&&"function"==typeof(r=n.constructor)&&r!==e&&l(o=r.prototype)&&o!==e.prototype&&Wt(t,o),t}(Xt?new Yt(r&&!o?t.source:t,n):Yt((r=t instanceof Zt)?t.source:t,r&&o?,e?this:Jt,Zt)},$t=function(n){n in Zt||qt(Zt,n,{configurable:!0,get:function(){return Yt[n]},set:function(t){Yt[n]=t}})},tn=Kt(Yt),nn=0;tn.length>nn;)$t(tn[nn++]);(Jt.constructor=Zt).prototype=Jt,at(b,"RegExp",Zt)}Ft=X("RegExp"),Bt=I.f,w&&Ft&&!Ft[Ht]&&Bt(Ft,Ht,{configurable:!0,get:function(){return this}});var en="toString",rn=RegExp.prototype,on=rn[en],cn=r(function(){return"/a/b"!{source:"a",flags:"b"})}),!=en;function an(t){try{var n=window[t],e="__exponea_storage_test__";return n.setItem(e,e),n.removeItem(e),!0}catch(t){return!1}}(cn||un)&&at(RegExp.prototype,en,function(){var t=u(this),n=String(t.source),e=t.flags;return"/"+n+"/"+String(void 0===e&&t instanceof RegExp&&!("flags"in rn)?},{unsafe:!0});var fn="exponea",ln=(an("localStorage"),an("sessionStorage"),"webxpClient");function sn(e,t){var r={_:[],__:!1};function n(t){return function(){if(""===e&&"initialize"===t&&arguments&&arguments[0].modify&&arguments[0].modify.overlay&&"loading"===document.readyState){var n="__inf__overlay__";document.write('<div id="'.concat(n,'" style="position:absolute;background:#fff;left:0;top:0;width:100%;height:100%;z-index:1000000"></div>')),setTimeout(function(){var t=document.getElementById(n);t&&document.body.removeChild(t),r.__=!0},arguments[0].modify.delay||500)}window.exponea._=window.exponea._||[],window.exponea._.push(["".concat(e).concat(t),arguments])}}for(var o=0;o<t.length;o++){var i=t[o];r[i]=n(i)}return r}window.exponea=sn("",["anonymize","initialize","identify","update","track","trackLink","trackEnhancedEcommerce","getHtml","showHtml","showBanner","showWebLayer","ping","getAbTest","loadDependency","getRecommendations","_bootstrapEditor","_insertExperiment","_revertExperiments"]),window.exponea.notifications=sn("notifications.",["isAvailable","isSubscribed","subscribe","unsubscribe"]);var pn=document.createElement("script");pn.type="text/javascript",pn.async=!0;var yn="https:"===document.location.protocol?"https:":"http:";pn.src="".concat(yn,"//"),document.getElementsByTagName("head")[0].appendChild(pn);var dn=window[ln]=window[ln]||{};dn.sdk=window[fn];dn.sdkObjectName=fn;dn.skipExperiments=true}();</script>
    "target": "<API-TARGET>",
    "token": "<PROJECT-TOKEN>",
    // your other custom options here
    webxpClient.path = '<API-TARGET>';
    webxpClient.sign = '<PROJECT-TOKEN>' + '/' + (/__exponea_etc__=([\w-]+)/.exec(document.cookie) || ['', 'new'])[1];
    webxpClient.src  = webxpClient.path + '/webxp/script/' + webxpClient.sign + '/modifications.min.js?http-referer='+encodeURIComponent(location.href.split('#')[0]);
    document.writeln(webxpClient.script = '<' + 'script type="text/javascript" src="' + webxpClient.src + '"><' + '/script>');
    <!-- do not delete this line --></script><script>
    !webxpClient.init && document.writeln(webxpClient.script.replace('/script/', '/script-async/').replace('><', ' async><'));


Things to replace in the integration snippet

You have to manually replace these 2 strings in the snippet:

  • <API-TARGET> (twice) - this is the API target URL, copy this from your existing integration snippet (this is the "target" in your old snippet). If your old integration snippet does not specify the API target, use the default of //
  • <PROJECT-TOKEN> (twice) - this is the project token in the form of AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE. Copy it from your previous integration snippet or find it in project settings in Exponea app.
  • The snippet inserted using Google Tag Manager or Adobe Tag Manager
    Also in these cases, it is necessary to insert the snippet straight into the header of the page, otherwise the non-flickering function won't work properly. If you needed to insert some attributes dynamically to the initialization of Exponea through GTM/ATM, move the initialization exponea.initialize({...}) into the used tag manager.


All in all, the non-flickering experiments significantly speed up the application of modifications on the webpage. The difference may even reach 4 seconds.

This new solution also speeds up the slow modifications as they work slightly faster compared to the original version. As a result, even if you use Jinja and customer filters, the overall time is shorter.

New Experiments

This feature is in Beta version

The New Experiments allow you to modify your website in a way when the user doesn’t notice it is done by a 3rd party tool - Exponea.

There are 2 challenges when applying modifications to the website that are ideally both solved in the same solution:

  • Flickering effect - website should be modified before it is presented to the customer so he can see the final version right from the beginning.
  • No website performance downgrade - we need to ensure that the website is not blocked for longer than predefined timeout (defaults to 4 seconds) duration and that the experiments are applied as soon as possible.

New Experiments were built to handle both challenges at once - apply changes without flickering and with no risk of blocking the website.


In cases where speed and reliability of the website may be disrupted, New experiments will fall back from sync to async. This may be due to e.g. high latency, high API response time or Internet connection problems.

How to setup the New Experiments:

There are 3 steps in the setup for the New Experiments:

  • JS SDK snippet update - this is a required step. New experiments won’t work if the new SDK snippet is not used.
  • CSS Style definition - this is a required step.
    Service worker setup - In case you want to use non-flickering experiments in sync blocking mode, this is a required step. Without service worker your customers will not have full non-flickering experience but the New experiments will work (with blinking between page views)

JS SDK Snippet Update

In order to launch the new version of Experiments, you have to first of all change the initialization snippet on which your website is running. To do this, change your initialization snippet by applying the ‘Use new experiments’ setting. (Find out more about about Integration)

Customizing the snippet:

This snippet introduces new option new_experiments which could either be true, false (for flickering experiments), or an object containing:

  • timeout - Controls the timeout of experiments in milliseconds. This is the maximum amount of time that the main page is hidden while the experiments load. After this timeout, the page is revealed even if the experiments have not finished loading yet. Defaults to 4000.
  • hide_class - CSS class name used to hide the main page content while the experiments are loading. Defaults to xnpe_async_hide.
  • mode - What mode to use to apply experiments. Sync blocks page rendering and requires a service worker. Async is simple to set up but does not block the rendering and might result in flickering, especially between page navigation in the web application which is not a single page . Defaults to async.

CSS Style update

For CSS Hiding to work properly, you have to put this new rule into your CSS:

.xnpe_async_hide {
    opacity: 0 !important;

When changing the hide class, you have to change it in the

Service worker setup

There can be only one service worker (file named service-worker.js in your webroot directory) for each website/domain. Find out more about Service worker setup.

Updated 7 days ago

Non-flickering Experiments

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.

We rely on cookies

to optimize our communication and to enhance your customer experience. By clicking on the Accept and Close button, you agree to the collection of cookies. You can also adjust your preferences by clicking on Manage Preferences. For more information please see our Privacy policy.

Manage cookies
Accept & close

Cookies preferences

Accept & close