NextJS Core Web Vitals - remove render blocking CSS

The ultimate NextJS Core Web Vitals guide - remove render blocking CSS

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

Remove render blocking CSS in NextJS

CSS is render blocking. This means that a browser will not start rendering when it finds CSS in the head of the page until all that CSS is parsed. If the browser is required to download one or more external CSS files that might take up precious time. In this article I will show you how to remove those render blocking CSS files in your NextJS app to speed up rendering. This will improve your paint metrics (First Contentful Paint and Largest Contentful Paint)


By default NextJS will create 2 stylesheets for a page. The first is the global stylesheet and the second on a page specific stylesheet. How much time will that cost us? That depends on a lot of factors like the visitors device, the network speed, the network connection protocol, the distance to the server, the size of the stylesheet etc. On average, for all my clients, across all devices I have found this takes about 200ms. On the NextJS website downloading those 2 stylesheets will take 600ms on a fast 3g connection. This will delay all the paint metrics by 600ms. Time to take action and remove these render blocking stylesheets!

nextjs css network

Option 1: Generate Critical CSS

The first and fastest option is to generate Critical CSS. Critical CSS is a collection of CSS rules that are needed to render the visible part of the page. Those rules are placed inline in the HEAD of the page. Then, in parallel, the original CSS files are downloaded while the browser will continue to render. Once the original CSS files are downloaded they are injected into the page.

Critical CSS is now available in NextJS as an experimental feature. Generating Critical CSS in NextJS is extremely simple. Just add experimental: { optimizeCss: true } to your next.config.js and add install critters@0.0.7 as a dependency in your project.

const nextConfig = {
  reactStrictMode: true,
  experimental: { optimizeCss: true }
}

Option 2: inline CSS in the HEAD of the page.

If you do not want to enable experimental features in your NextJS app to improve the Core Web Vitals you could consider inlining your CSS. You will need to create a custom head section and reference this in your _document.tsx.

The downside to this method is that the inline CSS will probably be larger then with the first method. However if you keep your stylesheets clean and effective this method will most probably improve the Core Web Vitals for your NextJS app significantly!

import { Html, Head, Main, NextScript } from "next/document";
import { readFileSync } from "fs";
import { join } from "path";

class InlineStylesHead extends Head {
  getCssLinks: Head["getCssLinks"] = ({ allFiles }) => {
    const { assetPrefix } = this.context;
    if (!allFiles || allFiles.length === 0) return null;
    return allFiles
      .filter((file: any) => /\.css$/.test(file))
      .map((file: any) => (
        <style
          key={file}
          nonce={this.props.nonce}
          data-href={`${assetPrefix}/_next/${file}`}
          dangerouslySetInnerHTML={{
            __html: readFileSync(join(process.cwd(), ".next", file), "utf-8"),
          }}
        />
      ));
  };
}

export default function Document() {
  return (
    <Html>
      <InlineStylesHead />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}  

Option 3: Styled Components

Styled Components is a CSS-in-JS tool that lets you write CSS in JavaScript files. This has many advantages. For example, you can repeat class names, easily remove unused CSS code, and manage code more easily compared to traditional CSS files. As for the Core Web Vitals it will mean you will only inject the styles that are needed on that page. Pretty neat right?

As of NextJS version 12.1 it is easier then ever to use styled components in your NextJS app. Add compiler:{styledComponents: true} to your next.config.js, install styled-components (yarn add styled-components and when it’s done installing, run yarn add -D @types/styled-components ), update the file _document.js to support server side rendering for styled components and you are good to go!

const nextConfig = {
    reactStrictMode: true,
    compiler: {
    styledComponents: true,
  }
}
import Document, { DocumentContext } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}

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 - remove render blocking CSSCore Web Vitals NextJS Core Web Vitals - remove render blocking CSS