How to improve Magento 2 Page Speed Insights Score?

Photo by Marc-Olivier Jodoin

Understanding the Page Speed Score

Google’s Page Speed Insights performance score is a summary of 6 main metrics:

  • First Meaningful Paint: measures when the primary content of a page is visible.
  • Speed Index: shows how quickly the contents of a page are visibly populated.
  • Time to Interactive: the amount of time it takes for the page to become fully interactive.
  • First CPU Idle: marks the first time at which the page’s main thread is quiet enough to handle input.
  • Max Potential First Input Delay: The maximum potential First Input Delay that your users could experience is the duration, in milliseconds, of the longest task.
  • Speed Index — 26.7%
  • First Contentful Paint — 20.0%
  • First CPU Idle — 13.3%
  • First Meaningful Paint — 6.7%
  • Max Potential First Input Delay — 0%
Magento 2.2.10 performance score, Slow 4G — 4x CPU Slowdown (Homepage)
191 out of 227 requests are JS files— 662 KB gzipped (Homepage)

Setting up our goal

So now we know that Google’s Page Speed score gives priority to sites that can be interacted with quickly, however there are so many scripts included by default in Magento 2 that no matter how minified, merged or bundled they are they still need a lot of time to load.

Remove as many scripts as possible…

And I don’t mean getting rid of a couple JS files here and there, I mean going for a deep clean and see how far that takes us.

Let the purge begin

Let’s start with the easy stuff, we can disable all the modules that we know we won’t use in our Magento 2 website. This might not remove any JS files since not all modules have frontend assets however it will potentially reduce the server response time which will improve all the Page Speed Score metrics proportionally.

  • Don’t care about swatches? Kill it ☠️
  • Amazon Payments? Kill it ☠️
  • Klarna? Kill it ☠️
  • Temando Shipping? WTF is that!? Bye ☠️

Upgrade to Magento ≥ 2.3.3

Earlier in the article I ran the initial Page Speed measurements on an “old” 2.2.10 version of Magento on purpose to showcase one of the main features introduced in 2.3.3.

Magento 2.3.3 performance score.
169 out of 206 requests — 607 KB gzipped.

Removing unused scripts

Now comes the fun part, out of all those 169 JS files how many of those is our website actually using?

  1. Scripts can have very obscure dependencies, even not so obvious ones.
Only 43% of JS code being used.
  • colorpicker.js: I can’t remember the last time I used a colorpicker in an e-commerce site. ☠️
  • datepicker.js: Same as above, I can think of more use cases for this but definetly not needed everywhere. ☠️
  • tooltip.js: This is used to render some information tooltips around the site, we use CSS for this functionality so no need. ☠️
  • polyfill.js: Looks like a polyfill for localStorage and sessionStorage for older browsers, this is also useful in some modern browsers (like Safari on iOS) where there’s no localStorage when using Incognito / Privacy Mode so it falls back to using cookies but in our case we don’t care about this feature. ☠
  • decorate.js: Used to add classes to lists, presumably for styling (first, odd, even, last). Maybe a remnant of the Magento 1 days when the CSS :nth-child selector wasn’t as widely supported. ☠️
Chrome search feature

Stripping down the minicart

While I was looking at the minicart component I realised there’s a lot of stuff there I wasn’t very emotionally attached to:

  • effects-fade and effects.js: Used to fade out items when removed from the cart, again not that bothered to lose that, if needed we can have it cheaper with a timeout + CSS transitions. ☠️
  • authentication-popup.js: Used to display a popup if you require login to checkout, case by case dependant but we can get rid of it for now, if needed maybe a redirect with a message might be enough. ☠️

Is the user going to be bothered by items not fading out when removed from the cart? Maybe not. Will they be bothered by the extra second it takes to load every single page because of this code? Probably.

Misc template changes

️Some scripts might need to be removed from .phtml template files, for example:

  • form-mini.js: We rarely use the default Magento search so no need to include this to handle autocompletion. ☠️
  • t️ranslate-inline.js: Used to render the UI to translate strings directly on the frontend, we never use this as all translations are done in the code level. ☠️

Replacing libraries

So far we’ve been removing unused script files but we can also replace some of the biggest libraries with some alternatives that will make the total request size smaller.

  • jquery.js: This is a bit scarier because jQuery is used EVERYWHERE but upgrading to jQuery 2.2.4 will reduce the library size by ~10%. This major version only dropped support for older browsers while keeping backwards compatibility so in theory (and in my limited testing) it should still work without major issues. ☠️
  • underscore.js: I didn’t actually change this one but I still thought it would be worth mentioning. In theory this library could get the same treatment as jQuery UI and split it into separate components (using something like lodash) since only ~40% is used at a time but it will require a lot of work and I don’t know if it’s worth for the sake of saving 5–6KBs. ⚠️

Going the extra mile

We’ve been focusing on JS since by know we know that’s what the browser struggles with the most, but we’re on a warpath here so we might as well get rid of other assets while we’re at it, eg:

  • Fonts: Sounds be obvious but let’s get rid of all the custom fonts and variations we’re not using. ☠️
  • Icons: We use SVG icons for these so we can get rid of the Luma/Blank icon font.

Measuring again

Let’s see how we look after doing all that work:

Post script removal performance score.
123 out of 153 requests — 433 KB gzipped.
  • 42% decrease in request count. 🎉
  • 42% decrease in JS bundle size. 🎉
  • Developer mode.
  • No composer optimisations.
  • No minification or caching.
  • No image lazy loading.
  • No CDN.
Lighthouse opportunities
  1. In my limited testing it hasn’t improved load times that much. I think people underestimate how good browsers are at painting pixels on the screen.
  • Add our own image lazy loading extension.
  • Switch Magento to production mode and optimise composer autoloader.
Post script removal + minification + lazy loading + composer optimisations.

Bundling Magento 2 JavaScript

Even though we’ve removed a lot of JS code we’re still loading hundreds of files per request, which wouldn’t be a massive issue if they were prefetched and loaded in parallel thanks to HTTP/2, but they’re not…

Final optimisation score with JS bundling.
4 out of 14 requests — 148 KB gzipped.

Closing thoughts

I’m sure there are ways to keep improving this score even more, I’ll keep working on this to get it closer to that sweet “100” but there are many areas of improvement. So for we only focused on JS and although it’s the biggest piece of the puzzle there are other ones that could use more attention (CSS, images, fonts, HTML).

Lead Developer at Media Lounge · 6x Magento Certified & Full Stack Developer · E-Commerce Specialist · Currently @ Bournemouth, UK

Get the Medium app