If you’ve built a React application using Server-Side Rendering (SSR), you’ve inevitably run into the frustrating Hydration Mismatch error.
This often happens when your component needs to render something that relies on browser-specific objects like window or localStorage.
The error occurs because the server, which renders your initial HTML, has no idea what window.innerWidth is.
When the client-side JavaScript loads and tries to “hydrate” (or attach interactivity to) the server-rendered HTML, it finds a structural difference in the DOM and throws a fit, often wiping the screen and starting over.
This defeats the purpose of SSR!
Fortunately, the fix is elegant and simple: defer all client-specific logic until after hydration is complete.
The Code Fix: useState and useEffect as Gatekeepers
This component structure uses a clever combination of the useState and useEffect hooks to safely access the browser environment without causing a server-client mismatch.
import React, { useState, useEffect } from 'react';
function LayoutCheckFixed() {
// 1. Initial State: The hydration "gate"
const [isMobile, setIsMobile] = useState(false);
const [hasMounted, setHasMounted] = useState(false);
// 2. useEffect: The client-only trigger
useEffect(() => {
// ONLY runs on the client, after the initial render.
const checkIsMobile = window.innerWidth < 768;
setIsMobile(checkIsMobile);
setHasMounted(true); // Open the gate!
}, []);
// 3. Conditional Render: The Hydration "Placeholder"
if (!hasMounted) {
return null; // Server and initial client render must match this!
}
// 4. Final Render: The stable, client-specific output
if (isMobile) {
return <div>Mobile Layout</div>;
} else {
return <div>Desktop Layout</div>;
}
}
🚀 How the Hydration-Safe Component is Processed
Understanding the sequence is key to mastering SSR. The code works because the server ignores useEffect, and the client’s initial render output matches the server’s output perfectly.
Phase 1: Server-Side Rendering (SSR) 🖥️
-
State Initialization:
isMobileisfalse, andhasMountedis false. -
useEffectIgnored: The server skips the entireuseEffectblock; it never tries to accesswindow.innerWidth. -
Conditional Check (Step 3): Since
hasMountedis false, the component immediately returnsnull. -
HTML Output: The server sends HTML containing an empty space (the result of rendering
null) for this component. This is stable and safe.
Phase 2: Client-Side Hydration 🌐
-
Client Initial Render: The client-side React code runs. It finds the same initial state (
hasMountedis false and also returnsnull. -
Hydration Success: React compares the client’s
nullwith the server’snull. They match perfectly! Hydration is successful, and the app becomes interactive. -
useEffectExecution: Only now does theuseEffecthook run.-
It safely checks the real
window.innerWidth. -
It sets the correct
isMobilestate. -
Crucially, it sets setHasMounted to true.
-
-
Final Re-Render: The state change triggers a final, correct re-render. The component skips the
return nullcheck and renders the final, stable, client-specific output (Mobile LayoutorDesktop Layout).
By delaying the logic that causes the mismatch, you ensure a smooth, single hydration pass, preserving the performance benefits of Server-Side Rendering. Happy hydrating!
Useful links below:
Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service
Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2
Best email marketing automation solution on the market! http://www.aweber.com/?373860
Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29
Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff
Buy me a coffee https://buymeacoffee.com/tyronneratcliff



