How to Fix FOUC (Flash of Unstyled Content) in Next.js 14 and 15
If you have ever watched a Next.js page load and seen a brief flash of unstyled content, you are not alone. FOUC, or Flash of Unstyled Content, happens when the browser renders HTML before the CSS has finished loading. In Next.js 14 and 15 with the App Router, this can happen for several reasons that are specific to how React Server Components and streaming work.
Most developers assume server-side rendering eliminates FOUC because the server sends fully styled HTML. But the reality is more nuanced. When you use CSS-in-JS libraries, dynamic imports, or certain font-loading strategies, the server-rendered HTML can arrive without its corresponding styles. The browser paints the page immediately, and then the styles arrive a split second later, causing that jarring visual flash.
The App Router in Next.js 14 introduced React Server Components by default. This changes how styles are collected during server rendering. Unlike the Pages Router, where _document.tsx gave you full control over style extraction, the App Router abstracts that away. Libraries like styled-components and Emotion had to release new versions specifically to support RSC. If you are on an older version, you will see FOUC.
Why FOUC Happens in Next.js App Router
The root cause is a mismatch between what the server renders and what the client hydrates. During server-side rendering, Next.js generates HTML with class names or style tags. But if the CSS extraction step happens after the HTML is streamed to the client, the browser paints the HTML before it knows how to style it.
With the Pages Router, Next.js used a static CSS extraction approach. The server would compile all the CSS, inject it into the head, and send a complete document. The App Router changed this. Now, styles can be streamed alongside content, which means the browser might receive content before it receives the corresponding styles. This is especially visible with nested layouts where child components load their CSS lazily.
Another common cause is third-party CSS or fonts. If you load Google Fonts, Font Awesome, or a Bootstrap CDN, those requests happen asynchronously. The browser does not wait for them to render the initial HTML. You get content without styling until those requests complete.
How to Detect What Causes Your FOUC
Open Chrome DevTools and go to the Network tab. Disable cache and reload the page. Look at the timeline: does the HTML arrive before the CSS? If yes, you have a style-loading problem. Next, check if you are using any CSS-in-JS library. Open the Elements tab and look at the head. Are the style tags present immediately, or do they appear after a delay?
You can also use the Performance tab to record a page load. Look for the First Paint event and compare it to when the CSS files finish loading. If First Paint happens before CSS is complete, that is your FOUC window. The goal is to eliminate that gap.
Fix 1: Update Your CSS-in-JS Library
If you use styled-components, make sure you are on version 6 or later. Version 5 does not support React Server Components properly. For Emotion, you need @emotion/react version 11.11 or newer. These versions include the extractCritical method that works with the App Router. After updating, restart your dev server and clear the .next folder.
For Tailwind CSS users: FOUC is rare because Tailwind generates static CSS classes. But if you are using Tailwind with dynamic class generation or the JIT compiler in an unexpected way, you might see flashes. Make sure you are using the latest Tailwind version and that your content paths in tailwind.config.js cover all files that use Tailwind classes.
Fix 2: Use next/dynamic with ssr: false
If the FOUC is limited to a specific component, you can defer its rendering to the client side. This is not ideal for SEO-critical content, but for UI elements like charts, carousels, or third-party widgets, it works well. Import your component with next/dynamic and set ssr: false. The component will only render on the client, so there is no server-client mismatch. This completely eliminates FOUC for that component because the styles load before the component mounts.
Fix 3: Use Loading Boundaries
The App Router supports loading.js files that show immediately while the page content streams in. If you wrap your content in Suspense boundaries, Next.js can send the loading state first and the actual content with its styles later. This does not eliminate FOUC entirely, but it masks it by showing a loading indicator or skeleton while styles load. Users see a smooth transition instead of a flash.
Create a loading.tsx file in your app directory for each route segment that experiences FOUC. Keep the loading component minimal to avoid its own FOUC issues. Inline critical CSS directly in the loading component using a style tag or Tailwind classes that are already in the initial CSS bundle.
Fix 4: Optimize Font Loading
Web fonts are a major FOUC trigger. When you load Google Fonts via @import or a link tag, the browser downloads the font file asynchronously. Text renders in a fallback font first, then swaps to the custom font when it loads. This causes a flash called FOUT (Flash of Unstyled Text). To fix this, use next/font instead of loading fonts externally. The next/font module downloads font files at build time and inlines them into the CSS, so there is no network request and no flash.
If you must use external fonts, add font-display: swap to your @font-face declarations, and use a matching fallback font that has the same metrics using the size-adjust property in CSS. Tools like font-style-matcher can help you find fallback fonts that minimize layout shift when the real font loads.
Preventative Measures
Keep your global CSS minimal. The fewer styles in your global stylesheet, the faster it loads. Use CSS Modules for component-level styles so they are code-split alongside their components. Avoid importing large CSS frameworks like Bootstrap entirely if you only use a handful of their utilities. Tree-shake your CSS with tools like PurgeCSS.
Finally, test on slow networks. Chrome DevTools lets you simulate 3G speeds. If your page looks good on fast WiFi but flashes on 3G, you have a FOUC problem worth fixing. Your users on mobile networks will thank you.