Hacking Next.js for better PageSpeed scores
We launched MEDWING’s new website in late 2019 with a lot of media content and animations on landing page. Thanks to Next.js’ ease of use and out-of-the-box optimizations, we received lots of positive feedback. However our marketing team were not so impressed with our below average PageSpeed score.
So, we set a goal to improve our PageSpeed Insights score and get it to the maximum achievable while keeping an acceptable UX.
And here are the results:
Previously it looked something like this:
Breaking it down
Key PageSpeed factors that affected the score drastically
- First contentful paint — Animations & images were the main culprits
- First CPU Idle — Without chunking enabled and minification there was a lot of JS for browser to parse and execute on page load.
- Time to Interactive — Main thread was busy handling JS and making it slow for the user
Things we investigated first
- CDN —We did setup CDN to decrease load on our application server — interestingly it lowered the score initially
- Chunking/code splitting — Applied multiple babel plugins to get better chunking and code splitting, dynamic imports helped. Granular chunking caused more harm than good.
- Webpack sideEffects — Setting side-effects to false caused the app to break/behave inconsistently. We kept it unchanged.
- Google Tag Manager — Staging env had a relatively better score than production, so GTM seemed to have an effect. Eventually, delaying GTM initialization worked.
Continued improvements
- Delaying non-critical JS
- Disabling animations on “above the fold content” to improve First Contentful Paint
- Bringing back CDN with all other tweaks — improved score this time
As Disabling animations and brining back CDN parts are self explanatory. I will instead expand on Delaying non-critical JS in the next section.
Hacking Next.js
I am sure you are wondering when I would bring up actually hacking Next.js. Well the moment has come.
Granular chunks and code splitting generate too many JS files. So, we decided to delay the load of as many of them as possible to free up browser resources (and to do better on PageSpeed score).
Find the clever hack below:
Add the customized NextScript
and Head
to your _document.js
to enable the hacks.
NextScriptCustom
overrides methods to enable delayed injection of non app breaking script tags. And HeadCustom
removes (or just returns nothing) preload link
tags for javascript and css.
Wrapping up
PageSpeed score does reflect real world stats. With increased PageSpeed score, mobile experience improved significantly. Generally, I assumed preloading JavaScript and CSS was a good idea for better user experience, but on mobile browsers that is not the case and is reflected in the PageSpeed score.
Edited by Eran Peer