Henry's Space

Solution to CSS Module FOUC Issue in Next.js

Next.js CSS FOUC 

Henry Hung

Henry Hung

Published 2022-10-14  •  Updated 2022-10-14  •  2.25 min read

Overview

When using CSS modules in Next.js, you might encounter an issue called FOUC (Flash of Unstyled Content). This issue causes the page to briefly display unstyled content before applying styles. This article will discuss how to solve this issue.

Problem

Until Next.js 14.2.6, when using the APP ROUTER, the prefetching of the <Link> component does not include CSS modules, prefetching only the HTML and JS content of the linked page. This causes the HTML content to be rendered into the DOM tree by the browser first when navigating with the <Link> component, and then it requests the CSS file from the server. During the request for the CSS, the page will display unstyled content, which is the FOUC issue. This problem was reported on Next.js' GitHub as early as 2018, but the official response was very dismissive, with at least three similar issues being closed without resolution. Disappointed by this, I attempted to solve this issue myself.

Solution

After three days of trials, I finally found an optimal solution based on dynamic import().

In general, we need to dynamically import all the CSS modules once in the RootLayout component. This ensures that all CSS modules are stored in the same CSS file. Consequently, when the initial screen loads, all CSS files are loaded in, effectively preventing the FOUC issue.

  1. Install the globby library:

    bash
    Copy Code
    npm i globby
  2. Modify the jsconfig.json file in the root directory to ensure src/../../ correctly points to the src folder in the project root directory:

    json
    Copy Code
    {
        "compilerOptions": {
            "baseUrl": "./",
            "include": ["./"]
        }
    }
  3. Insert the loadAllCssUnderAppDirectory() method into the root layout. This method uses globby to get all paths to .module.css files under src/app/, and then imports them in the root layout. This makes the root CSS file include all CSS from src/app/, fetching all CSS files upon the initial screen load and thereby avoiding the FOUC issue. Using template strings is necessary because Next.js' webpack requires paths to be static, with at least the first two segments being static, or it will throw an error. If you have .module.css files in other places such as src/components/, you can additionally create a loadAllCssUnderComponentsDirectory() method.

    javascript
    Copy Code
    const loadAllCssUnderAppDirectory = async () => {
        const pathPrefix = 'src/app/';
        const pathSuffix = '.module.css';
        // Get all CSS module paths
        const paths = await globby(`${pathPrefix}**/*${pathSuffix}`);
        // Dynamically load all CSS modules
        for (let path of paths) {
            const truncatedPath = path.slice(pathPrefix.length, -pathSuffix.length);
            (await import(`src/app/${truncatedPath}.module.css`)).default;
        }
    };
    
    const Layout = async ({ children }) => {
    
        await loadAllCssUnderAppDirectory();
    
        return (
            <html>
                <body>
                    {children}
                </body>
            </html>
        );
    };
    
    export default Layout;