Next-plugins: how to clean browser css cache

Created on 7 Feb 2018  Â·  7Comments  Â·  Source: vercel/next-plugins

Hi @timneutkens

In production the stylesheet is compiled to .next/static/style.css. You have to include it into the page using either next/head or a custom _document.js. The file will be served from /_next/static/style.css

usually we will use hash code to clean browser cache if css file has any modification, like style.41d523.css

how can we do this on this plugin?

Most helpful comment

Here's one more discovery that might help those who have little CSS in style.css. That's the case when most of the styles come from styled-components or a similar framework. Turns out you can improve your website's performance by including the contents of .next/static/style.css directly into document, e.g. like this:

let style = null;
if (process.env.NODE_ENV === "production") {
  style = fs.readFileSync(`${process.cwd()}/.next/static/style.css`, "utf8");
}

// inside _document's Head:
{typeof style === "string" ? (
<style dangerouslySetInnerHTML={{ __html: style }} />
) : (
<link rel="stylesheet" href="/_next/static/style.css" />
)}

This helped me change the lighthouse performance score in production from 89 to 96 because _First meaningful paint_ time decreased from 2.06 to 1.10 seconds. This is an advice in the original report that I followed:

screen shot 2018-03-26 at 11 31 08

When a stylesheet is heavy, it's cheaper to load it as a separate file and cache that request in the browser – this keeps the size of the server-rendered HTMLs small while avoiding extra round trips in consequent page requests. However, when style.css contains just normalize.css and a couple of simple global selectors, always keeping the entire stylesheet within the document's <head> and avoiding extra round trips appears to be a better thing. Obviously, caching is no longer a problem, because <style>{style}</style> is read from .next/static/style.css when your Next.js server starts.

Don't forget to import styles in a higher order component that works as a page wrapper:

# e.g. in hocs/page.js
import "./path/to/your/root/stylesheet.css"

Can this issue be considered as resolved?


If you want to check your website's performance, simply run

npm install --global lighthouse

lighthouse http://example.com/ --view

if ...style.css` screws up syntax highlighting in your editor, here's a trick:

let style = null;
if (process.env.NODE_ENV === "production") {
  // ${"css"} prevents editors from incorrectly highlighting code after css`
  style = readFileSync(`${process.cwd()}/.next/static/style.${"css"}`, "utf8");
}

All 7 comments

I ended up doing the following in _document.js - this works for the time being, however we need to make sure the BUILD_ID is the same on a multi-deployment basis!

Note this builds from a src directory!

import Document, { Head, Main, NextScript } from 'next/document';
import { readFileSync } from 'fs';

let version = '';

if (process.env.NODE_ENV === 'production') {
  version = `?v=${readFileSync(`${process.cwd()}/src/.next/BUILD_ID`)}`;
}

export default class extends Document {
  render() {
    return (
      <html>
        <Head>
          <link rel="stylesheet" href={`/_next/static/style.css${version}`} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}

It's also possible to use file's hash instead of BUILD_ID as version:

import { createHash } from "crypto";
import { readFileSync } from "fs";

// ...

let version = "";
if (process.env.NODE_ENV === "production") {
  const hash = createHash("sha256");
  hash.update(readFileSync(`${process.cwd()}/.next/static/style.css`));
  version = `?v=${hash.digest("hex").substr(0, 8)}`;
}

// ...

If the styles don't change between the builds, there's no need to download them and so everyone is better off.

In general, it's more efficient to calculate file cache using streams, but this should be unnecessary in our case. if .next/static/style.css is big enough to cause a hiccup during server setup, something is probably wrong 😃

crypto.createHash has been a part of Node's core nearly since the beginning of times, so there is no need to install a new module.


What are the caveats of this trick? If anyone sees them, could you please share?

Even though the workarounds in two previous comments solve the issue with style.css caching, all other static assets like images remain under the over-caching thread.

It'd be truly great to bring some webpack magic here! This would not only allow correct caching by turning paths into something like image.eld7hd4p.png, but would also make it possible to store images in folders with components. It's a bit of a shame that the images have to go to static in next.js, which separates them from a place where they really belong.

Here's one more discovery that might help those who have little CSS in style.css. That's the case when most of the styles come from styled-components or a similar framework. Turns out you can improve your website's performance by including the contents of .next/static/style.css directly into document, e.g. like this:

let style = null;
if (process.env.NODE_ENV === "production") {
  style = fs.readFileSync(`${process.cwd()}/.next/static/style.css`, "utf8");
}

// inside _document's Head:
{typeof style === "string" ? (
<style dangerouslySetInnerHTML={{ __html: style }} />
) : (
<link rel="stylesheet" href="/_next/static/style.css" />
)}

This helped me change the lighthouse performance score in production from 89 to 96 because _First meaningful paint_ time decreased from 2.06 to 1.10 seconds. This is an advice in the original report that I followed:

screen shot 2018-03-26 at 11 31 08

When a stylesheet is heavy, it's cheaper to load it as a separate file and cache that request in the browser – this keeps the size of the server-rendered HTMLs small while avoiding extra round trips in consequent page requests. However, when style.css contains just normalize.css and a couple of simple global selectors, always keeping the entire stylesheet within the document's <head> and avoiding extra round trips appears to be a better thing. Obviously, caching is no longer a problem, because <style>{style}</style> is read from .next/static/style.css when your Next.js server starts.

Don't forget to import styles in a higher order component that works as a page wrapper:

# e.g. in hocs/page.js
import "./path/to/your/root/stylesheet.css"

Can this issue be considered as resolved?


If you want to check your website's performance, simply run

npm install --global lighthouse

lighthouse http://example.com/ --view

if ...style.css` screws up syntax highlighting in your editor, here's a trick:

let style = null;
if (process.env.NODE_ENV === "production") {
  // ${"css"} prevents editors from incorrectly highlighting code after css`
  style = readFileSync(`${process.cwd()}/.next/static/style.${"css"}`, "utf8");
}

If you're going with the inlining approach, you may need to use:

<style dangerouslySetInnerHTML={{ __html: style }} />

Otherwise, things like quotes get converted to HTML entities.

Thank you @bryandowning, you are right! I updated the comment and removed <style>{{ style }}</style>.

Will be possible when https://github.com/zeit/next.js/pull/4119 lands for Next 6

Was this page helpful?
0 / 5 - 0 ratings

Related issues

popuguytheparrot picture popuguytheparrot  Â·  4Comments

pencilcheck picture pencilcheck  Â·  4Comments

suppayami picture suppayami  Â·  3Comments

krokhale picture krokhale  Â·  3Comments

harrysolovay picture harrysolovay  Â·  4Comments