This article looks at how the anti-flicker snippets used by A/B testing tools like Optimizely and Adobe Target can negatively impact web performance.
After explaining the problem, we'll look at potential solutions that minimize flicker while also keeping site speed in mind.
What are anti-flicker snippets?
A/B tests and other customizations mean that the content on a website depends on the visitor who's viewing it. These customizations are usually implemented through a dedicated A/B testing service, in order to let marketing teams run tests without having to get developers involved.
The visitor's browser first downloads the page with default content, then the browser loads the list of customizations from the A/B testing service, and finally applies them to the page.
However, these customizations introduce flicker. The visitor first sees the default content, and then the content disappears and is replaced.
An anti-flicker snippet is a piece of code that prevents flicker by hiding the original page content until the customizations have been applied.
This graphic shows filmstrips indicating the rendering progress of a website in three scenarios:
- Without A/B testing
- With A/B testing and flicker
- With A/B testing and an anti-flicker snippet
What's the problem with anti-flicker snippets?
Hiding content ensures that content customizations don't negatively impact the user experience when they are applied.
However, hiding content with anti-flicker snippets also causes a worse user experience by making the site load more slowly. Visitors to your site spend more time looking at an empty page.
How do anti-flicker snippets impact Core Web Vitals?
Anti-flicker snippets increase your Largest Contentful Paint (LCP) metric. This can also impact your Google rankings, as LCP is one of the Core Web Vitals that's used to assess site experience.
Applying customizations without an anti-flicker snippet can cause content to shift around on the page, if the custom content has a different size than the default content. These shifts in turn increase the Cumulative Layout Shift metric, another Core Web Vital.
So when optimizing your anti-flicker logic you need to balance these two competing concerns. I would lean towards first sorting out LCP issues, and then reviewing and addressing layout shifts one by one.
How can you fix poor performance caused by anti-flicker snippets?
To fix site speed issues caused by A/B testing tools you can:
- Run A/B tests server-side
- Configure the snippet to only hide some content
- Optimize how quickly the A/B tests load
- Accept the flicker instead of fighting it
- Disable A/B testing entirely on some pages
Server-side customizations and A/B testing
The reason anti-flicker snippets are necessary is that the web server first returns default content and then another tool modifies the content on the client.
If you're able to run implement A/B tests on the server this problem can be avoided entirely. However, this is likely difficult to implement.
Customize the anti-flicker snippet
Anti-flicker snippets typically hide all content in the HTML
body tag. This is a drastic solution, and it's done because the snippet does not know which parts of the page will be modified until the customizations have been loaded.
However, as the person running the A/B tests, you will know more about what type of tests you run. Do you test different page headings? Then hide only
h1 tags. Are you customizing call-to-action copy on a
button? Then hide button as well.
In contrast, you might not be running any customizations on
p tags or content in the website header. So these components don't need to be hidden while the customizations are being loaded.
You could still end up with layout shifts, for example if your customized
h1 stretches over two lines instead of one. But a small layout shift might be ok, and is less jarring than page content getting swapped out. If it is a problem, specify a minimum height for your
h1 and make sure it is always large enough to handle different content lengths.
Load customizations more quickly
The process of how an A/B testing tool loads customizations might look like this:
- Load a tag manager
- Load additional code for the A/B testing tool
- Make a fetch request to get a unique ID for the user
- Use that unique ID to load the customizations
- Apply the customizations
This process is often sequential and involves establishing connections to multiple servers. Like any other request chain on your website it can be optimized, though this may be harder as you don't have full control over how the third-party works.
Browser resource hints can be a useful tool to optimize sequential request chains that cannot be parallelized.
For example, if the customizations are loaded from c.abtesting.com, you add a preconnect hint for that domain to your document. The browser will establish a server connection before the actual fetch request. That way, when the fetch request is made, only one network round trip is needed, as the existing connection can be used.
Accept the flicker
If your users find that your site loads slowly, you might want to consider just accepting flicker when it happens.
This especially applies in these two cases:
- you only run tests on a few pages at a time, but the anti-flicker snippet is loaded globally for your site
- the tests you run don't target the most prominent page content, but instead tweak small UI components or customize below the fold content
Abandon A/B testing
Once you look into it, it might turn out the A/B testing tool isn't actually used very often. In that case you can just remove the tool from your website.
Many A/B testing tools have pages explaining the negative speed impact of anti-flicker code and how to mitigate it:
Analyzing a real-world example of the impact of an anti-flicker snippet
As an example of what hiding body content looks like in practice, let's inspect the Asana homepage and look at how the data in the filmstrip seemingly conflicts with the request waterfall.
The request waterfall shows that:
- The last render-blocking request finishes after about 0.6 seconds
- The LCP image has loaded after 3.1 seconds
Yet, the filmstrip shows no content until 5.7 seconds after the page was opened.
Looking a bit deeper, we find that Asana uses Google Optimize. It also looks like Optimize only starts loading relatively late. I haven't confirmed this, but there might be a sequential request chain involving multiple Google Tag Manager requests.
The HTML document contains styles that hide the body content, and the
async-hide class is applied to the
opacity: 0 !important
If we manually override those styles we can see that the page now starts to render after just 2.1 seconds, instead of the 5.7 seconds from before.
Anti-flicker snippets on DebugBear
DebugBear not only monitors site speed over time but also automatically detects when page content is hidden by an anti-flicker snippet.
Track Core Web Vitals and see how your anti-flicker optimizations impact performance.