Hydration Error with Tables in NextJS.
September 22, 20234 min read
NextJS hydration error is like the inevitable death of the universe. It will happen eventually.
I ran into one recently with HTML tables. I am going to be talking about it today.
I will explain what the error was and how I fixed it. There is also a bit of a deep dive into why the error happened in the first place.
After writing the syntax for my HTML table, NextJS threw the error:
Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <td> in <td>. [See more info here:](https://nextjs.org/docs/messages/react-hydration-error)
I was lost. I followed the MDN docs for creating the table. MDN is the gold standard for web dev doc so I was sure I had done everything right.
The syntax was correct, the DOM elements was in the right order...but were they?
The DOM nesting was in fact not correct.
The faulty code was a line in the
<tbody> <td> <td>Kehinde Adeleke</td> </td> <tbody>
I had a nested a
td in another
td which led to the hydration error. After removing the nested
td, the error was fixed.
This fix looks easy now but this frustrated me for 3hrs. Because of that, I decided to do some more research.
Why this happened
I wasn't satisfied with just solving this. I wanted to get to the root of the problem.
The HTML5 spec on
td element was a good place to start.
td element can only be used in a context where it is the child of a
tr element. This context is a non-normative description of the
NextJS renders the HTML statically on the server first and I hypothesize that
SSR cannot work with improper tag nesting as per the spec above.
Due to this, the runtime throws an error - honestly this should be a server error that properly specifies what the error is. A better error message of
invalid DOM Nesting would aid developers - certainly would have aided me - debug the problem faster.
Update: There is some improvements that Tim from the Vercel team described here. The component stack is included which now helps
There is a second part to this. The Browser still constructs a table. I believe the client tries as much as it can to still create a working document even if the tags aren't ordered properly.
If you construct a
HTML table like this:
<table> <tr> <th>Company</th> <th>Contact</th> <th>Country</th> </tr> <tr> <td>Alfreds Futterkiste</td> <td>Maria Anders</td> <td>Germany</td> </tr> <tr> <td>Centro comercial Moctezuma</td> <td>Francisco Chang</td> <td>Mexico</td> </tr> </table>
Your Client will display it alright but NextJS will throw an error in the format:
Unhandled Runtime Error Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <tr> in <table>.
The error is different from the one above but the root cause is the same. The DOM nesting is incorrect.
The solution is to include the
<tbody/> tags. This is the correct way to construct a table:
<table> <thead> <tr> <th colspan="2">The table header</th> </tr> </thead> <tbody> <tr> <td>The table body</td> <td>with two columns</td> </tr> </tbody> </table>
Next time you run into an error like this, check your DOM nesting. It might be the cause of the error.
I hope this helps someone out there. If you have any questions, feel free to reach out to me on Twitter.