Skip to main content
Skip to main content

Luke Howsam

Software Engineer

Preventing fouc with Chakra UI

Logo of Chakra UI
Published
Share

How to fix FOUC with Chakra UI & Next.js

Chakra UI is a modular component library which under the hood uses Emotion & styled system. I like using Chakra UI if I want to quickly build a UI for prototype purposes. In addition to this, I typically reach for Chakra UI because it is:

  • CSS-in-JS based
  • Easily customizable
  • Written in Typescript
  • Not too bloated

If you've used emotion and Next.js, there is a high chance you've encountered FOUC (flash of unstyled content).

This happens because the <ChakraProivder /> stylesheets are only loaded on the client. Since Chakra UI uses Emotion, the solution is to render the styles on the server-side & cache them using additional Emotion packages, @emotion/cache & @emotion/server.

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

# npm 
npm i @emotion/cache @emotion/server

After adding these dependencies, we need to create a cache for emotion to utilize:

// src/utils/cache.ts 

import createCache from "@emotion/cache";

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

Now we can render these stylesheets on the server-side in src/pages/_document.tsx with Document.getInitialProps:

import { ColorModeScript } from "@chakra-ui/react";
import createEmotionServer from "@emotion/server/create-instance";
import Document, {
  DocumentContext,
  DocumentInitialProps,
  Head,
  Html,
  Main,
  NextScript,
} from "next/document";
import emotionCache from "../utils/cache";
const { extractCritical } = createEmotionServer(emotionCache);

export default class CustomDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    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>
    );
  }
}

Now that this is in place, the page should render the chakra UI stylesheets on both the server-side & client-side (and the FOUC issue should be fixed!).