It has never been a better time to be a frontend developer. Frameworks are more performant than ever. While I got started on my JAMStack journey on Gatsby, I have since moved onto Next JS as it is constantly being iterated on and has a lot of nice to haves.

But with anything it depends on how you use the tools your given. It can quickly blow out leaving you scratching your head as to why your bundle size is so big. Here is how I recently optimised a Next JS website.

1. Preact

Preact is a fast 3kB alternative to React with the same modern API. Compared to React + React Dom, which is 42kB, that is a huge saving in JavaScript computation. You can follow this guide on how you can replace React with Preact using Webpack aliasing.

Be sure to test your website thoroughly as there is a chance it might not play nice with other packages.

2. Dynamic components

If you need components to load based on user actions or certain pages, use next/dynamic. This will allow you to code split that component so that it loads as a chunk. This will save you kB on your first page load.

Examples of dynamic components include:

  • Modals
  • Popups
  • Page builder sections

3. react-lazyload

Like how you lazy load images, you can also do the same thing for components on a page.

react-lazyload allows you to load components as they scroll on the page.

4. Use the 'sizes' prop on next/image

By default, Next JS uses the value '100vw' on this prop. While this is fine for full screen images, you will end up with the 'Properly size images' warning when it comes to images that are constrained. By setting the 'sizes' prop, you will be loading the right sized image.

5. Analyse your starter code

To quickly get running, most of us like to start off with a Next JS starter as they integrate with different CMS', style frameworks and etc. As you build you might start to realise you do not need certain NPM packages.

For instance, the starter that I used for this website came with a math library. Now unless you are a math professional, remove that as it is taking so much kB to load!

6. Bundlephobia

NPM packages are a beautiful thing as we have a registry of tools waiting for us to add to our projects. While there are notable libraries which everyone uses for a certain use case, you should consider using a lightweight alternative if you do not need a full feature set.

When searching for packages, you can use Bundlephobia to see the cost of adding it to your bundle and lighter alternatives.

For example, Swiper JS is an excellent library for creating sliders which comes in at 38.1 kB. Swiper has features like 3D effects and parralax transitions to name a few. But if I just need a simple product slider with dots at the bottom, I could use Keen-Slider which is only 5.7 kB, a huge saving.

7. Webpack Bundle Analyzer

This plugin will allow you to visualise the size of your Next JS website. You should focus on the size of the 'app.js' file as this gets loaded on every page. It is useful to find libraries that should not be loaded or packages that do not do a good job of tree shaking.

It might be hard to find where the libraries are loaded, so play around with your app and make changes then rebuild to find the problem.

8. Critical CSS

Critical CSS refers to inlining css that's used by above the fold content. It helps reduce the first contentful paint times.

You can add Critical CSS using a flag in your next config. This tweet explains how you can do so. This can help with achieving faster paint times.

9. Script 'worker' strategy

We have all had our pagespeed score go down when adding third party libraries. This worker strategy allows us to put these scripts in their own web worker allowing the main thread to focus on the UI.

Under the hood, this is powered by Partytown which is still early in its life. So, if you have the time, investigate this strategy.

Otherwise, use the 'lazyOnload' strategy which loads the scripts after the page is idle.

10. Variable fonts

Next JS already includes a way to optimize fonts from Google which reduces an extra request to Google's server for the font styles. But unfortunately trying this for a variable font does not work as intended. It still loads the font styles individually rather than using the variable font.

There is a work around available using the following steps:

  • Download the variable font from Google fonts
  • Store the font in the /public/fonts directory
  • Add a <style jsx global> tag to your _app.js which has the @font-face declaration. You could also store it inside a stylesheet but this would add to the critical rendering path.
  • Add a link preload tag to the head tag of _document.js

Using all these strategies I was able to decrease the First Load JS for _app.js:

From 156 kB to 67.5 kB