Client hydration is not blocking above-the-fold content
Why it matters
Setting ssr: false on above-the-fold components — the header, hero, and navigation — means the server sends empty HTML for those regions, and users see a blank viewport until JavaScript hydrates. This damages First Contentful Paint (FCP) and the perceived speed of the page, even when the full page eventually loads quickly. Browser pre-rendering and crawler indexing also fail for these regions: search engine snapshots taken before hydration miss the content entirely. ISO 25010 performance-efficiency.time-behaviour defines time-behaviour as the time from user action to visible response — a blank viewport is a failure of that response.
Severity rationale
Low because hydration blocking typically causes a brief flash of blank content rather than a total failure, but on slow connections it extends to several seconds of blank above-the-fold space.
Remediation
Remove ssr: false from above-the-fold components. Server-render the static structure of the header and hero, then hydrate interactive elements after paint. Reserve ssr: false for components that genuinely cannot run on the server (browser-only APIs, WebGL, certain chart libraries).
// Before — header blank until JS hydrates
const Header = dynamic(() => import('./Header'), { ssr: false })
// After — server renders static header structure immediately
import Header from './Header' // static import, SSR-compatible
// Interactive elements inside Header hydrate after paint
// Pattern: server-render shell, hydrate interactive parts lazily
// app/layout.tsx
export default function Layout({ children }) {
return (
<>
<Header /> {/* server-rendered static markup */}
<main>{children}</main>
<DynamicChatWidget /> {/* ssr: false acceptable — below fold, not critical */}
</>
)
}
const DynamicChatWidget = dynamic(() => import('./ChatWidget'), {
ssr: false,
loading: () => null
})
Detection
-
ID:
hydration-strategy -
Severity:
low -
What to look for: Count all components rendered above the fold (header, hero, navigation) on the primary landing pages. For each above-the-fold component, check whether it uses
ssr: false,dynamic(() => ..., { ssr: false }), or requires full JavaScript hydration before any content is visible. Classify each as: (a) renders static HTML first, hydrates after paint, or (b) requires JS before any content appears (blank until hydration). Enumerate: "X above-the-fold components found, Y render without requiring JS hydration." -
Pass criteria: At least 90% of above-the-fold components render static HTML before hydration. No more than 1 above-the-fold component uses
ssr: false. Static HTML is served first on all primary landing pages; hydration happens after content is painted. Report: "X of Y above-the-fold components render server HTML before hydration." -
Fail criteria: 2 or more above-the-fold components require full JavaScript hydration before any content is visible, or any primary landing page is blank until JavaScript loads.
-
Skip (N/A) when: Framework does not use hydration (pure server-rendered without client JS, or pure CSR where this check is covered by
no-client-only-seo). -
Detail on fail:
"3 of 5 above-the-fold components use ssr: false — header, hero, and nav are blank until JS loads"or"Dashboard layout uses ssr: false for sidebar and chart — 2 above-fold components block rendering" -
Remediation: Use lazy hydration or defer non-critical interactivity:
// Before — blocks hydration export default function Dashboard() { return ( <> <Header /> {/* Heavy interactive component */} <Content /> </> ) } // After — static content first, interactivity deferred export default function Dashboard() { return ( <> <StaticHeader /> {/* Just static markup */} <Content /> <DynamicHeader client:load /> {/* Hydrate dynamically */} </> ) } // Or lazy hydrate non-critical components const LazyInteractiveWidget = dynamic(() => import('./Widget'), { loading: () => <Skeleton />, ssr: false })
External references
- iso-25010:2011 · performance-efficiency.time-behaviour — Time Behaviour
Taxons
History
- 2026-04-18·v1.0.0·Initial import from performance-load·automated