Avoid Pre-loading Scripts With Next.js

Next.js pre-loads the site JavaScript out-of-the-box. This how-to details how to remove this pre-loading.

Last Updated : May 14, 2021


What it means?

If your website runs on the Next.js framework, Next.js controls how the site's scripts are loaded. As part of this, Next.js automatically pre-loads the site scripts. To see this in action, just open the 'Network' tab of your Chrome browser devtools and load up your website. You shall be able click on the link in the 'Initiator' column for any of your scripts to see the <link rel="preload" ...> in action. The 'High' network priority is also resulting from the preload statements.

Next.js script preloading in action
Next.js script preloading in action (Chrome Devtools -> Network Tab)

What is the potential downside of this behavior?

A potential downside of this default behavior is that it can negatively affect the download speed of other on-page assets. For one of our websites, we had set the on-page hero image to preload. This was to improve our LCP (Largest Contentful Paint) Core Web Vitals. But, Next.js was also pushing half a dozen script requests to pre-load. This left little bandwidth room for our on-page hero image to load faster.

Is the Next.js script preloading configurable?

As of Next.js 10.0.0 (in May, 2021), Next.js provides no way to stop the site-scripts from pre-loading. This has become important with the weightage Google Search is putting behind the Core Web Vitals and the LCP.

As a result, untill Next.js makes this configurable, this has to be done by overriding Next.js's default <Head /> (where Next.js sets up these preloads).

How to stop the Next.js scripts from preloading?

Modifying the default Next.js <Head /> behavior shall require us to create custom <Head /> behavior within our site's _document.tsx. The most reliable way to achieve this is to refer the Head class within the Next.js _document.tsx. Next.js uses getPreloadMainLinks to set-up the scripts pre-loading. By overriding this function, we can modify the required functionality.

import Document, {
  Head,
  Main,
  NextScript,
  DocumentContext,
} from "next/document";
import * as React from "react";
function getOptionalModernScriptVariant(path: string): string {
  if (process.env.__NEXT_MODERN_BUILD) {
    return path.replace(/\.js$/, '.module.js')
  }
  return path
}
class NoPreloadHead extends Head {
  getPreloadMainLinks(): JSX.Element[] | null {
    const { assetPrefix, files } = this.context._documentProps
    const { _devOnlyInvalidateCacheQueryString } = this.context
    const preloadFiles =
      files && files.length
        ? files.filter((file: string) => {
            // `dynamicImports` will contain both `.js` and `.module.js` when
            // the feature is enabled. This clause will filter down to the
            // modern variants only.
            return file.endsWith(getOptionalModernScriptVariant('.js'))
          })
        : []
    return !preloadFiles.length
      ? null
      : preloadFiles.map((file: string) => (
          <link
            key={file}
            nonce={this.props.nonce}
            //rel="preload" //We comment this to avoid pre-loading.
            href={`${assetPrefix}/_next/${encodeURI(
              file
            )}${_devOnlyInvalidateCacheQueryString}`}
            as="script"
            crossOrigin={
              this.props.crossOrigin || process.env.__NEXT_CROSS_ORIGIN
            }
          />
        ))
  }
}

Now, instead of using the default Next.js <Head />, we use our <NoPreloadHead />.

export default class MyDocument extends Document<any> {
    render() {
    return (
      <html>
        <NoPreloadHead />
        <Main />
        <NextScript />
      </html>
    );
    }
}

How to verify the changed behavior?

With this change in place, our site scripts should load with 'Low' priority. Also, the scripts may appear later in the page-loading network waterfall because they shall load later in the page-loading sequence. This shall make available additional bandwidth for resources we have prioritized to load earlier.


Site scripts loading without Next.js preloading
Site scripts loading without Next.js preloading (Chrome Devtools -> Network Tab)

Punit Sethi

About the Author

Punit Sethi has been working with large e-Commerce & B2C websites on improving their site speed, scalability and frontend architecture. He tweets on these topics here.

Also Read