FOUC (Flash of Unstyled Content) is a common but frustrating visual glitch that can happen in modern React apps — especially in Next.js App Router — where your layout appears unstyled briefly during page load or refresh.
What is FOUC?
FOUC happens when the browser renders HTML before your CSS or styles are applied — resulting in a flash of unstyled or partially styled elements.
In SSR frameworks like Next.js, this often happens due to mismatches between the server-rendered HTML and client-rendered UI.
Symptoms of FOUC
On refresh, your UI shows raw HTML (unstyled) for a moment before styles kick in.
CSS variables like --primary-color aren't applied until the page fully loads.
-The warning: Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties.
Common Causes of FOUC in Next.js App Router
- Dynamic Theming (e.g., per-tenant styles via Redux/localStorage) When you load styles after hydration, like setting a theme color based on the current tenant:
useEffect(() => { document.documentElement.style.setProperty('--primary-color', tenantColor); }, []);
This runs only after the page loads, so the browser renders unstyled HTML first → FOUC.
Using Date.now(), Math.random(), or typeof window in shared components
These values differ between server and client → causes hydration mismatch, and sometimes FOUC.3rd-Party CSS Libraries Not Loaded Early Enough
If you're importing styles from UI libraries (e.g., PrimeReact, Tailwind CSS) inside components, they are loaded too late — causing unstyled content to flash on screen momentarily.
// Inside a component file (not recommended) import 'primereact/resources/themes/lara-light-indigo/theme.css';
How to Fix FOUC in Next.js App Router
1.Always Import Global CSS in layout.tsx
app/layout.tsx import "primeflex/primeflex.css"; import "primereact/resources/themes/lara-light-indigo/theme.css"; import "./globals.scss"
This ensures styles are loaded before your app renders.
2.Provide Fallback Theme Styles in globals.scss
:root { --primary-color: #ff8c00; // fallback before tenant-specific color is applied }
This avoids flashing unstyled buttons or layouts before tenant styles load.
3.Wrap Client-Side Logic in a Hydration Guard
Use this in a client component (e.g., HydrationWrapper.tsx):
"use client"; import { useEffect, useState } from "react"; export default function HydrationWrapper({ children }) { const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); if (!mounted) return null; return <>{children}</>; }
Then use it in layout.tsx:
<body> <HydrationWrapper> {children} </HydrationWrapper> </body>
This delays rendering until the client takes control — preventing mismatches.
Bonus: Avoid These Anti-Patterns
❌ Avoid dynamic CSS imports.
❌ Don’t use Date.now() or Math.random() directly in render.
❌ Don’t read from localStorage or window in server components.
❌ Don’t conditionally render <html> or <head>
in client components.
FOUC is annoying — but totally fixable in Next.js App Router. By carefully separating your server logic from client-specific theming or rendering, you can eliminate style flashes, avoid hydration warnings, and ensure a polished user experience.
Top comments (0)