CSS Modules break selective hydration in React 18 & 19 causing FOUC #77239
Labels
CSS
Related to CSS.
Lazy Loading
Related to Next.js Lazy Loading (e.g., next/dynamic or React.lazy).
linear: next
Confirmed issue that is tracked by the Next.js team.
Pages Router
Related to Pages Router.
React
Related to React.
Link to the code that reproduces this issue
Pages Router: https://stackblitz.com/edit/stackblitz-starters-h9tuzhzs
App Router: https://stackblitz.com/edit/stackblitz-starters-u1jswo3f
To Reproduce
1.1 (pages router) Stackblitz
1.2. (app router) Stackblitz
2.1 (pages router) See that the Footer beeing NOT red for 5 seconds
2.2 (app router) See that the Footer is NOT using the SSR but rendering the fallback
LazyHydrationCss.mp4
The key elements of the reproduction:
A main page that lazily loads a footer component inside Suspense and uses CSS Modules:
Current vs. Expected behavior
React 18 introduced selective hydration, a powerful feature that improves performance by prioritizing the hydration of critical UI elements. However, when using this feature with CSS Modules in Next.js Pages Router, it causes a Flash of Unstyled Content (FOUC).
The issue arises because Next.js does not recognize that a lazy-loaded component was rendered during SSR and therefore loads the associated CSS lazily as well. While the component's HTML is correctly rendered server-side and the class name is applied, the CSS is not loaded until the component hydrates on the client, causing a visual flash.
Current Behavior
Expected Behavior
This issue effectively breaks one of the key benefits of React 18's selective hydration feature - which is to provide a seamless experience where pre-rendered HTML looks identical to the hydrated component while JavaScript loads in the background.
Why This Matters
Selective hydration is one of React 18's most powerful features for improving perceived performance. It allows:
When working correctly, selective hydration allows sites to deliver complex interfaces that are visually complete and stable on initial load, with interactivity progressively enhancing as JavaScript loads
The current issue forces developers to choose between:
Provide environment information
Operating System: Platform: darwin Arch: arm64 Version: Darwin Kernel Version 24.3.0: Thu Jan 2 20:24:16 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6000 Available memory (MB): 65536 Available CPU cores: 10 Binaries: Node: 20.18.2 npm: 10.8.2 Yarn: 1.22.22 pnpm: 10.2.0 Relevant Packages: next: 15.3.0-canary.12 // Latest available version is detected (15.3.0-canary.12). eslint-config-next: N/A react: 18.2.0 react-dom: 18.2.0 typescript: N/A Next.js Config: output: N/A
Which area(s) are affected? (Select all that apply)
CSS, Lazy Loading, App Router, Pages Router, React
Which stage(s) are affected? (Select all that apply)
next build (local), next start (local), next dev (local), Vercel (Deployed), Other (Deployed)
Additional context
More information about selective hydration:
React 18 introduction to selective hydration: https://youtu.be/pj5N-Khihgc?t=688
Demonstration of selective hydration advantages: https://www.youtube.com/watch?v=pj5N-Khihgc&t=966s
React Working Group discussion: reactwg/react-18#37
The text was updated successfully, but these errors were encountered: