NextJS Core Web Vitals - fix third party scripts

The ultimate NextJS Core Web Vitals guide - fix Core Web Vitals issues caused by third party scripts

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

Fix third party Scripts in NextJS

Third party Scripts are scripts that add third-party functionality to your websites. Often these scripts are hosted by a third party although this is usually not strictly necessary (you can, for example self-host the analytics javascript file). Third-party scripts provide a wide range of useful functionality, making the web more dynamic, interactive, and interconnected. These scripts may be crucial to your website's functionality or revenue stream. But third-party scripts also come with many risks that should be taken into consideration to minimize their impact while still providing value.


nextjs css network

Just imagine: You are the proud owner of an optimized Next.js site. You have optimized all of your code, implemented some form of static generation, optimized all your images, implemented Critical CSS but your site still does not pass the Core Web Vitals. What is going on?

It might be because of third party scripts like ads, analytics, social-media buttons, widgets, A/B testing scripts, video players and so on.

How Third Party Scripts Impact the core web Vitals

Third Party Script can mess up your Core Web Vitals in more ways then you can probably imagine. These are some of the problems I came across on live websites.

  • Slow down the network. Third party scripts can send multiple requests to multiple servers, download large unoptimized files like images and video's, download frameworks and libraries several times.
  • Third party JavaScript can block the DOM at any time (or even several times during a page visit), delaying how quickly pages can render and use too much CPU time with can delay user interaction and cause battery drain.
  • Render blocking third-party scripts loaded can be a single-point of failure (SPOF)
  • Third party scripts can cause network issues duo to poorly configured HTTP caching, lack of sufficient server compression and slow/old http protocols
  • Harm user experience in many other ways like hiding content, block browser events (like the window load event) or use outdated api's like document.write

Fix third-party scripts and Core Web Vitals in Next.js

Ideally, you’ll want to ensure third-party script is not impacting the critical rendering path. Your go-to solution would be to use the defer or async attribute. Unfortunately, timing-wise , this is not a good option for most Next.js sites. A Next.js site relies healivy on JavaScript to hydrate the page.

This means that as soon as the Next.js bundles have downloaded the browser will run that JavaScript. This takes up time and resources. This process will slow down when the browser is too busy compiling and running third party JavaScripts.

Introducing the Next.js Script component

The Next.js Script component, next/script, is an extension of the HTML <script> element. It enables developers to set the loading priority of third-party scripts anywhere in their application, outside next/head, saving developer time while improving loading performance.

To add a third-party script to your application, import the next/script component:

import Script from 'next/script'

Strategy

With next/script, you decide when to load your third-party script by using the strategy property:

There are three different loading strategies that can be used:

  • beforeInteractive: Load a script before the page is interactive
  • afterInteractive: Load a script immediately after the page becomes interactive
  • lazyOnload: Load a script during idle time
  • worker: Load a script in a web worker

beforeInteractive Strategy

Scripts that load with the beforeInteractive strategy are injected into the initial HTML from the server with the defer attribute enabled and run before self-bundled JavaScript is executed.

From a Core Web Vitals perspective this is exactly the type of behavior we would like to avoid! The beforeInteractive strategy should only be used on scripts that are absolutely critical to the page. Third party scripts are not ever supposed to be critical!

<Script
  id="bootstrap-cdn"
  src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
  strategy="beforeInteractive"
 />

In this case the the bootstrap JavaScript library is added to the generated html with the defer attribute just before the next.js bundles. This means the browser will probably fetch and execute the bootstrap library before the next.js bundle. This will delay the next.js hydration and probably block them main thread when the browser should be downloading and running the next.js chunks. For the Core Web Vitals this means the First Input Delay will probably be affected.

afterInteractive Strategy

Scripts that use the afterInteractive strategy are injected client-side and will download and after Next.js hydrates the page.

From a core web vitals perspective this is a much better (but not yet perfect) place to inject third party scripts. This strategy should be used for scripts that do not need to load as soon as possible and can be fetched and executed immediately after the page is interactive.

<Script
  strategy="afterInteractive"
  dangerouslySetInnerHTML={{
    __html: `
    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
    })(window,document,'script','dataLayer', 'GTM-XXXXXX');
  `,
  }}
/>

lazyOnload Strategy

With the lazyOnload strategy things are finally becoming interesting! The way I think about third party scripts is simple: 'they should not be critical to a page'. If you cannot live without a certain third party script you should probably not rely on a third party.

Scripts that use the lazyOnload strategy are loaded late after all resources have been fetched and during idle time. This means that the third party script will not interefere with the next.js chunks and will mimize the impact this script has on the first input delay (FID)

<Script
  id="some-chat-script"
  src="https://example.com/chatscript.js"
  strategy="lazyOnload"
 />

worker Strategy

The worker strategy is an experimental feature that uses partytown https://partytown.builder.io/ to act as a proxy between your scripts and a web worker. The concept is interesting but at the moment probably not production ready since it is still in beta. My advice on the worker strategy for the moment is to stay away from this until either the project has matured or the DOM will become available to web-workers.

I help teams pass the Core Web Vitals:

lighthouse 100 score

A slow website is likely to miss out on conversions and revenue. Nearly half of internet searchers don't wait three seconds for a page to load before going to another site. Ask yourself: "Is my site fast enough to convert visitors into customers?"

NextJS Core Web Vitals - fix third party scriptsCore Web Vitals NextJS Core Web Vitals - fix third party scripts