Delay Google Analytics to improve Pagespeed Insights score

I noticed recently that Google Analytics will knock down the PageSpeed Insights score of this site from 100, to around 90-95.

This isn't terrible - anything above 90 is still considered good.

However, keeping a fast website is important for SEO, and even a small change may affect search engine rankings.

And since PageSpeed Insights is a Google utility, it's possible (if not likely) that the same algorithm is used when considering ranking.

Can we download the script asyncronously?

When getting the script from Google, they will apply the async attribute by default:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-E1RENC0QRH"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-E1RENC0QRH');
</script>

In doing so, the script will download whilst the page is parsing, and pause parsing to execute the script.

This is not ideal. We don't need the script in order to render the page, nor do we need the script to be loaded before the page is run.

Can we defer the script?

Alternatively, we could change the attribute to defer:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-E1RENC0QRH"></script><script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-E1RENC0QRH');
</script>

Now, the script will download whilst the HTML is parsing, and only execute once the page has finished loading.

This is an improvement! For your users, there will be no additional delay to load your page.

Does this fix PageSpeed Insights?

Unfortunately, the PageSpeed Insights score will still be less!

This is because it will wait until the document is loaded, and then continue to wait a little longer until any additional scripts have finished.

So how do we get around this?

Forcing Google Analytics to load later

Counterintuitively, the solution for getting a higher score is to force a delay, then load the script.

We can also load the script in response to a user event, since PageSpeed will not perform these:

// Load the script after the user scrolls, moves the mouse, or touches the screen
document.addEventListener('scroll', initGTMOnEvent);
document.addEventListener('mousemove', initGTMOnEvent);
document.addEventListener('touchstart', initGTMOnEvent);

// Or, load the script after 2 seconds
document.addEventListener('DOMContentLoaded', () => { setTimeout(initGTM, 2000); });

// Initializes Google Tag Manager in response to an event
function initGTMOnEvent (event) {
	initGTM();
	event.currentTarget.removeEventListener(event.type, initGTMOnEvent);
}

// Initializes Google Tag Manager
function initGTM () {
	if (window.gtmDidInit) {
	  // Don't load again
	  return false;
	}

	window.gtmDidInit = true;
	
	// Create the script
	const script = document.createElement('script');
	script.type = 'text/javascript';
	script.onload = () => { 
	  window.dataLayer = window.dataLayer || [];
	  function gtag(){ dataLayer.push(arguments); }
	  gtag('js', new Date());
	  gtag('config', 'G-E1RENC0QRH');
	}
	script.src = 'https://www.googletagmanager.com/gtag/js?id=G-E1RENC0QRH';
	
	// We are still deferring the script
	script.defer = true;
	
	// Append the script to the body of the document
	document.getElementsByTagName('body')[0].appendChild(script);
}

This will either download the script immediately after the user scrolls, moves the mouse, or touches the screen.

Otherwise, the script will wait 2 seconds to load (anything less than 2 seconds seemed to get picked up by PageSpeed insights).

The downside

The only disadvantage I've found with this solution is that it may not record some users.

If the user loads the page, and then leaves within 2 seconds without interacting with the page, this won't get recorded.

Personally, I'm ok with this. If a user does do this, I wouldn't get much useful information anyway!