Pixel Imperfect
In order to assess performance, customer-facing websites oftentimes necessitate tracking pixels/tags in order to gather analytics, marketing, and tracking data. These pixels are typically <script>
tags installed in the <head>
of the page. Tag managers like Google Tag Manager (GTM) enable non-engineers to manage these tags without having to deploy any code, instead allowing pixels to be added or removed with an easy-to-use web interface. While a boon to marketing and product teams for Not Having to Involve Engineering™️, tag managers can be a bit of a nightmare for tech teams trying to run down front end performance issues. Here are some lessons learned from the past couple months of pixel performance sleuthing on our main car shopping page.
We were able to eliminate “hanging” behavior and improved our First Input Delay (FID) which is a measure of how responsive the page is to input from the user. Slow FID makes a user think the page is not working. For a user to feel that the page is responsive, the target value for FID is 100ms or less. On mobile in particular, we had poor performance for FID, measuring around 129ms for 75th percentile (P75), with a max of 1,285ms! All our customers are important to us, and many of our customers are on mobile devices, so it was important for us to address this issue. Once we addressed our pixel pain, our 75th percentile FID went from 129ms to 45ms. 📉 🎉
TL;DR/Lessons Learned
- Third-party pixels, even when loaded
async
, are loaded concurrently with the rest of the page’s resources and are using browser resources. If they are slow to load or are large, they will absolutely slow down core web vitals like Largest Contentful Paint (LCP), First Input Delay (FID), and Time to Interactive (TTI). Once third-party pixels are loaded, they can block the main browser thread. This means that the page can “hang” and become unresponsive. - Furthermore, pixels that listen to DOM manipulations (like many user tracking and marketing pixels) can have a substantial impact on these metrics, even after initial page load, and can cause poor UX from layout thrash. All told, these impacts can be large enough to your bottom line/conversion rate that they might undo the point of the pixel in the first place, so it’s worth scrutinizing performance.
- Define a process with tag manager stakeholders (engineering, product, and marketing) to assess the impact of new pixels and routinely review existing pixels to determine whether they are still needed (and whether they are deprecated!)
- Running Lighthouse tests on your app can help you identify third-party libraries that could be impacting page performance. Additionally, both Chrome and Safari have excellent profilers that can be used to investigate network requests and layout thrashing. Many pixels themselves load other pixels (!): it’s helpful to look at network requests and what changes in the
<head>
of your page upon page load.
Hanging pages, tag managers, and third-party tracking pixels
For weeks, we had been trying to run down an issue some users were experiencing with our shopping page where the page would hang, become very slow, or unresponsive. This seemed to happen primarily to users using ad-blockers. In profiling our mobile shopping experience on Safari, I also experienced similar frustratingly slow response times. We couldn’t find any obvious culprits profiling the app in local development, and finally — defying conventional wisdom — started wondering if there was something about production that could be slower than development.
We noticed that, for some users, third party scripts would get stuck loading and never finish. Noticing a plethora of pixels being injected into the page on load, we started looking into tag managers that were being used in our app. We first discovered issues with the tag managers themselves: both GTM and Segment were being used as tag managers — in some cases duplicating each other — and GTM was being loaded both by our app and by Segment. Simply removing GTM from our app and only loading it with Segment decreased our 75th percentile load time by from 4.5s to 2.8s!
Once we got our tag managers straightened out, we started inventorying pixels being loaded from tag managers. We had some really valuable learnings:
- Script tags, even when loaded
async
(which GTM does by default) are loaded concurrently with the rest of the page’s resources and are using browser resources. Once they are loaded, they will initialize right away, regardless of what else is still being loaded on the page. If a script is slow to initialize, it can block the main browser thread, rendering the page unusable. We witnessed this with some third-party libraries which, once initialized, themselves loaded several more scripts. While these scripts were being loaded, they used enough browser resources that things like fonts and images needed for the page to function could not themselves finish loading. - Pixels that track user interactions (often used by product or engineering teams to quantify end user performance metrics or see how a user interacts with the page) listen to DOM manipulations. Depending on how the pixels are implemented, they can [ironically] have a substantial impact on end user experience. We noticed layout thrash while profiling our page triggered by these libraries tracking button clicks, filter interactions, and the like.
- Running lighthouse tests, we could see that several pixels contained unused and un-minified code. One example is a library providing surveys which were only being triggered a small percentage of the time. We were suffering a significant performance penalty for a pixel that was not even needed all of the time.
While we weren’t able to remove many pixels needed by product and marketing teams, we were able to provide feedback to help make some of our third party vendor’s tooling more performant. We also found opportunities to remove pixels internal teams no longer needed. One such pixel turned out to be the root of a lot of our issues: removing it improved our mobile FID by 65%! 💸