Prevent FOUC on Next.js + Chakra UI

TL;DR, use @emotion/cache and @emotion/server to prevent initial unstyled content

Posted on August 13th, 2021 / Last updated on January 4th, 2022



January 2022 Update: Previously, the demo for this post was using my previous iteration of my personal website. But since my website has been updated, I had to either publish the previous version just for this post or create a separate repo for demonstrating the FOUC (flash of unstyled content) (I chose the latter).

For some reason I cannot replicate the flashing issue.

Maybe it has been fixed for the latest Next.js or Chakra UI. Maybe I am using Next.js experimental configurations. I haven't pinpoint the actual reason why. Both the site with cache and site without cache has the same behavior. Only the flashing when changing the last known theme occurs, which is known by the Chakra UI team.

So that means this post is somewhat invalid for my initial issue, but maybe it'll be useful for some cases. I'll keep this post for archival purposes and also update all references to the new repository and website.


If you're using Chakra UI with Next.js, chances are that you'll encounter an initial white flash or some parts still unstyled before the full content loads. This is known as FOUC (flash of unstyled content). Here's an example of this website with the FOUC issue:

Preview of flash of unstyled content for this website
Preview of flash of unstyled content for this website

This happens because the Chakra UI stylesheets from <ChakraProvider /> are not loaded server-side and only client-side. The solution, since Chakra UI is based on Emotion, is to render the styles server-side and cache it using two additional Emotion packages, @emotion/cache and @emotion/server.

# using yarn
yarn add @emotion/cache @emotion/server

# using npm
npm install @emotion/cache @emotion/server

After adding those dependencies, first make the Emotion cache instance, preferably on a separate source file, e.g. lib/emotion-cache.ts (example source file for this website):

import createCache from "@emotion/cache";

export default createCache({
  key: "css",
});

Then, render the stylesheets in pages/_document.tsx via Document.getInitialProps and use the previously made cache instance with @emotion/server's extractCritical (example source file for this website):

import * as React from "react";

import emotionCache from "@/lib/emotion-cache";

import { ColorModeScript } from "@chakra-ui/react";
import createEmotionServer from "@emotion/server/create-instance";
import Document, { DocumentContext, Head, Html, Main, NextScript } from "next/document";

const { extractCritical } = createEmotionServer(emotionCache);

export default class CustomDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const initialProps = await Document.getInitialProps(ctx);
    const styles = extractCritical(initialProps.html);
    return {
      ...initialProps,
      styles: [
        initialProps.styles,
        <style
          key="emotion-css"
          dangerouslySetInnerHTML={{ __html: styles.css }}
          data-emotion-css={styles.ids.join(" ")}
        />,
      ],
    };
  }

  render() {
    return (
      <Html lang="en">
        <Head>
          <meta charSet="UTF-8" />
          <meta content="ie=edge" httpEquiv="X-UA-Compatible" />
        </Head>

        <body>
          <ColorModeScript />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

With that, the page should now render the stylesheets both server-side and client-side. Here's an example of this website after adding the solution:

Preview of cached stylings for this website
Preview of cached stylings for this website

TL;DR, use @emotion/cache and @emotion/server to prevent initial unstyled content. Theoretically this should also work on Next.js static exports if you're using next export. You can see the full project for this website on GitHub.

Hope this helps! 🙌🏻


This is also cross-posted on dev.to and Medium.