No history yet

Common Causes of Hydration Errors

Transcript

Beau

Okay, so last time we talked about this… this magic trick, basically. Hydration. Where the server sends this static, lifeless HTML page, and then React on the client-side just breathes life into it, attaches all the little strings to make it interactive.

Jo

Exactly. It's like receiving a mannequin that's perfectly dressed, and your job is just to hook up the joints so it can move. That's what React is doing on the client.

Beau

Right. But what happens when the mannequin arrives with, I don't know, a different hat than the instructions say it should have? What happens when that magic trick goes wrong?

Jo

That is the perfect analogy, actually. Because that's exactly what a hydration error is. It's a mismatch. React gets this HTML from the server and it has its own blueprint—the component code—of what it *expects* to see. And when the two don't line up, it... well, it complains. Loudly.

Beau

So it's not just about attaching the strings anymore. It's about checking if the mannequin is even the right one.

Jo

Exactly. Before it attaches anything, it does a check. It looks at the HTML from the server and compares it to what it would have rendered itself on the client. The most basic cause of an error is just... a straightforward structural mismatch. The server sent a div, but your client-side code renders a p tag. Or the server sent a tag with a certain CSS class, but the client code doesn't.

Beau

Okay, that seems… almost too simple. Like, how does that even happen? Don't you use the same code on both the server and the client?

Jo

You do! But sometimes there's logic that causes a difference. Imagine you have a component that's supposed to render a table. Maybe on the server, for some reason, the list of data to create the table rows is empty. So it renders an empty table. But on the client, you immediately fetch the data, so your code tries to render a table full of rows. React looks at the empty server table and the full client table and says, 'Whoa, these are not the same thing.'

Beau

Ah, okay. So even though it's the same component, the *state* it's in is different between that first server render and the first client render. That makes sense. It's like one has an empty box and the other has a box full of stuff. Different things.

Jo

Precisely. But it gets even trickier with what you could call dynamic content discrepancies. This is a super common one. Let's say you want to display the current time on your page. 'This page was loaded at 10:30:05 AM'.

Beau

Sure, yeah. Classic website footer stuff.

Jo

Okay, so the server, let's say it's in London, renders that page. It looks at its clock, generates the HTML with '10:30:05 AM', and sends it over the internet to a user in New York. By the time it arrives and the user's browser starts to hydrate, a few milliseconds have passed. The client's React code runs, and it also tries to render the current time. But now, it's '10:30:06 AM'.

Beau

And... mismatch. React expected to see a five at the end, but it got a six. I see. So anything that's time-sensitive, or even something like generating a random number, would cause this.

Jo

Random numbers are a perfect example. The server runs `Math.random()` and gets 0.4. It puts that in the HTML. The client runs `Math.random()` and gets... well, probably not 0.4. And you get a hydration error.

Beau

So basically, any piece of code that will definitely produce a different result on the server versus on the client during that initial render is a landmine for hydration.

Jo

Yep. Which brings us to the third big one, which is kind of related: using browser-specific APIs during server rendering.

Beau

What does that mean? Like, things that only exist in Chrome or Firefox?

Jo

More fundamental than that. Things that only exist in a browser, period. Think about the `window` object. Or `document`. Or `localStorage`. These are things you use all the time in client-side JavaScript.

Beau

Okay… so what's the problem?

Jo

Well, your server... it's not a browser. It's a Node.js environment, most likely. It doesn't have a 'window'. It doesn't have a screen to measure the width of. So if your component's rendering logic says something like, 'if `window.innerWidth` is less than 500 pixels, render the mobile view, otherwise render the desktop view'...

Beau

Oh, I see. The server just goes... what's a window? It'll either crash, or... I guess it would just render the desktop view by default? The 'else' case?

Jo

Exactly. Let's say it renders the desktop view. It sends that HTML to the user, who happens to be on their phone, which has a screen width of, say, 390 pixels. Their browser gets the desktop HTML, but when React starts to hydrate, it runs the same logic. It checks `window.innerWidth`, sees 390, and decides to render the *mobile* view.

Beau

And there's your mismatch again. The server sent a big desktop component, but the client expected a small mobile one. That's a really sneaky one, because the code is identical, it just runs in completely different environments with different capabilities.

Jo

It's probably the most common category of hydration errors people run into when they're starting with server-side rendering. You just forget that your code is running in two places now, and one of those places is a dark, windowless box.