Preventing Flash of Unstyled Content(FOUC) in Next.js Applications

Artur Kulembetov
3 min readApr 21, 2024

--

Preventing Flash of Unstyled Content(FOUC) in Next.js Applications

As web developers, we often face the challenge of the Flash of Unstyled Content (FOUC). This issue is not just a minor annoyance — it can significantly impact user experience and perceived performance. In this article, I’ll break down the common reasons why FOUC occurs, particularly in Next.js applications, and share a solution that I’ve implemented in my own projects to handle it.

What Causes FOUC?

  1. Asynchronous Loading of CSS: The most common cause of FOUC is when your CSS does not load at the same time as your HTML. In the case of server-rendered applications like those built with Next.js, stylesheets are often loaded asynchronously to improve load time. This can mean that the HTML content is visible before the CSS is fully applied.
  2. JavaScript Execution Delays: When your application’s styles are managed or applied through JavaScript — such as through CSS-in-JS libraries or inline style manipulation — there can be a delay in execution. This delay occurs because the browser needs to fully download, parse, and execute the JavaScript that applies the styles.
  3. Render-Blocking Resources: Other resources, such as external scripts or fonts, can block the rendering process. If these resources take precedence or delay the loading of CSS, the content will appear unstyled momentarily.
  4. Client and Server Rendering Mismatch: Next.js uses server-side rendering (SSR) for faster initial page loads. However, if the initial render from the server does not perfectly match the rehydration on the client-side due to different states or dynamic content, it can cause FOUC. This mismatch can force the client to re-render parts of the DOM, which may appear briefly unstyled.

Practical Solutions to Prevent FOUC

  1. Inline Critical CSS

For immediate page rendering without FOUC, consider inlining the critical CSS directly into the HTML. This ensures that the essential styles are loaded with the HTML content. You can automate this in Next.js using libraries that extract and inline critical CSS during the build process.

2. Preload Important Resources

To ensure that stylesheets and other important resources are available as soon as possible, use <link rel="preload"> tags in your <Head> component for CSS and fonts. This tells the browser to prioritize these resources during the loading phase.

3. Custom Hook

I’ve developed a custom React hook, useTheme. This hook helps ensure that the theme is applied seamlessly and promptly as the application loads, preventing any flash of unstyled or incorrectly styled content.

import { useEffect } from 'react';

function useTheme() {
useEffect(() => {
// immediately set the theme on the first client-side load
const currentTheme = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('theme', currentTheme);

// apply a visibility class to the body to ensure there's no FOUC
document.body.classList.add('body-visible');

return () => {
// clean up by removing the class if the component unmounts
document.body.classList.remove('body-visible');
};
}, []);
}

export default useTheme;

Add the following CSS to ensure that the body remains hidden until the theme is properly loaded.

body:not(.body-visible) {
visibility: hidden;
}

Integrate the useTheme hook within your main layout component to ensure it is applied across your entire application.

import useTheme from './hooks/useTheme';

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
useTheme();

return <div>{children}</div>;
};

4. CSS Loading Strategy Using React Context

For global style management, leveraging React’s Context API has streamlined my approach, maintaining consistency across all components and preventing FOUC.

import React, { createContext, useContext, useState, useEffect } from 'react';

const StyleContext = createContext();

export const StyleProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
useEffect(() => {
const storedTheme = localStorage.getItem('theme') || 'light';
setTheme(storedTheme);
}, []);

return (
<StyleContext.Provider value={{ theme, setTheme }}>
{children}
</StyleContext.Provider>
);
};

export const useStyle = () => useContext(StyleContext);

And there you have it — simple and effective ways to prevent those jarring flashes of unstyled content. It’s all about making your site’s first impression as smooth as possible. So go ahead, keep building those beautiful websites, and enjoy the seamless style that comes with mastering FOUC prevention. Your users will definitely appreciate the polished experience!

If you’ve enjoyed this journey and are curious to see what else we can create together, or if you have ideas to share, follow me on GitHub:

--

--

Responses (1)