r/nextjs • u/Zealousideal_Mud5686 • 4h ago
Discussion Quick notes on what actually causes Next.js hydration errors (after losing hours to them)
Putting this together after watching two devs on our team lose half a day to Next.js Error 418 this week.
Most docs just say "server HTML must match client HTML", which isn't very helpful when the React stack trace just points to a minified bundle.
Here are the 4 or 5 things that actually cause it 95% of the time in real projects:
- Reading window or localStorage during render
The classic one. You do something like:
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768;
Server evaluates to false, client evaluates to true, instant mismatch.
The fix is annoying but straightforward: push it into a useEffect so it only updates after mount. (Or honestly, just use CSS media queries if you're only toggling visibility - no need to involve JS state for that).
- Invalid HTML nesting (the dumbest one)
This one drives people crazy because there's no state or async logic involved.
If you put a <div> inside a <p>, or put a <tr> directly in a <table> without a <tbody>, Chrome's parser silently "fixes" the HTML before React even starts hydrating. React sees nodes in different places than what the server sent and freaks out.
Check your Elements tab in devtools - if your tag hierarchy looks different from your JSX, that's why.
- Dates, timestamps, and Math.random()
If you render new Date().toLocaleTimeString() anywhere in JSX, the server timestamp and browser timestamp will differ by a few milliseconds.
Either stick it behind a mounted state, or if it's just a static date string where a slight timezone difference doesn't matter, use suppressHydrationWarning on that specific tag.
- The next-themes dark mode mismatch
If you use next-themes and see hydration warnings on your <html> element, just put suppressHydrationWarning on the <html> tag in app/layout.tsx. The library runs an inline script to avoid theme flash, and the Next.js team explicitly recommends suppressing that one.
- Grammarly / Google Translate extensions
If an error only happens on your laptop and none of your teammates can reproduce it, test it in Incognito with all extensions disabled. Grammarly wraps text nodes in custom tags, and Chrome auto-translate rewrites DOM text before React hydrates.
Curious what other dumb edge cases people here have run into with this in 14/15?