Speeding up Next.JS websites by removing JavaScript

Psst: we are hiring remote frontend developers and backend developers.

Illustration of a person holding a laptop and a person holding a picture of cat

JAMStack is now more popular than ever due to its promise to deliver better performance and scalability than the traditional server rendered websites. Still, when comparing performance to a traditional server-rendered site, many JAMStack sites lose, leaving the end-users worse off. Why is this happening? Shouldn’t the JAMStacks advantage of delivering HTML from CDN outbeat everything?

The modern JAMStack approach

A big part of the typical JAMStack site is usually made with one of the popular frontend frameworks, like Next, Gatsby or Nuxt. As all of these frameworks enable developers to create rich, interactive websites with pre-rendering of pages, the HTML being pushed to the CDN edge.

With these frameworks comes fantastic flexibility for the frontend developer, but also great responsibility. It’s important to know that the framework is doing two completely different things:

  1. Prerendering. Creating pre-rendered HTML pages, with CSS and JS, ready to be pushed to a CDN
  2. Interactivity. Making these pre-rendered pages com alive on the device by the use of the framework runtime Javascript

The problem

The modern JAMstack approach looks like the best of both worlds, and in many cases this is true.

There is however a hidden problem lurking beneath the surface.

A bunch of websites get a big performance penalty in step 2, interactivity. It is often overlooked when developing a website since it typically affects mid-range devices on mobile networks, not what the typical developer is testing on.

How big is the problem? Let's use Pagespeed Insights as the tool to measure performance and the blog you are reading right now as the example. Here’s how it performs with framework enabled interactivity:

PageSpeed 63 score screenshot

A score of 63 is not terrible, but when looking at the lab results, it’s clear that we can do better.

PageSpeed detailed results screenshot

The Single Page Application (SPA)

We’re using Next.js and in this step, the framework JavaScript is downloaded and the application is rehydrated with the state coming from the server, essentially re-creating the HTML structure once more which enables Single Page Application (SPA) mode.

This takes up CPU resources on the device, as well as additional download costs as the framework files and application files need to be acquired. This process can take several seconds to complete and is especially prominent when the visiting user is on a mid-range mobile phone and on a mobile network.

Look what happens if we turn off step 2, and deliver just HTML and CSS:

PageSpeed 99 score screenshot

Now that's a massive difference. The reason it’s scoring so much better is because it no longer take up that much resources on the device:

Lighthouse results details screenshot


Does enabling framework runtime JavaScript justify the performance penalty?

In our case it was easy to answer. No. The blog post you are reading is a barebone HTML document, sprinkled with a minimum amount of javascript to enable the few interactive things we have, like the main navigation.

This is what gives the best performance and user experience for our visitors.

Speed illustration

Users don’t want JavaScript

In an ecommerce store, users want to find and buy things. At a blog, users want to read.

Every website offers different things and its frontend should be tailor made to serve a user experience best suited to its visitors. Frameworks with runtime JavaScript is just one way to enable that.

Take this website as an example. We still use Next.js for all the pages, enabling incremental static builds and the wonderful flexibility for composing user interfaces that React provides, and we have made the decision to disable the framework runtime JavaScript for almost all pages, except the very few where it made sense to keep it.

This is not an article making an argument against a frontend framework running on a device, it is great for:

  • Faster route transitions
  • Complex client side logic
  • Development speed

How do you know when to do what? I would argue that if there is not a whole lot of complex interactivity, which admittedly is best handled by a frontend framework, then you should not enable the framework runtime JavaScript at all.

Multichannel illustration