-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: validateDOMNesting
Hydration failed
#24519
Comments
validateDOMNesting
error produces Hydration failed
on SSR validateDOMNesting
error produces wrong HTML nodes in SSR
This seems to be expected behavior? SSR cannot work with improper tag nesting as per the HTML spec, the browser's parser won't accept it, and the whole point of SSR is to have the browser parse the initial document structure for you without using any JS. Client side rendering can work with improper tag nesting because the DOM apis do allow creating these invalid nestings "by hand" (with There is no way to create a raw HTML document that nests a So this situation is a warning for client side rendering, an error for SSR, and a warning for hydration; but the only way to continue the "hydration" is to blast away all of the DOM so far because it will be wrong due to invalid nesting. |
Yeah, I mostly agree, and after going through very long discussion threads, it does seem that it is developers fault for not fixing warnings/errors... However, it might be worth having clearer communication with regards to the implications of invalid DOM nesting, maybe it's already there and I just haven't seen it. |
I would expect SSR on the server produce a server-console warning that it encountered invalid html nesting and modified html output and a remark that this could cause hydration errors on the client side. |
This is one of the weak points of Next.js. Not communicating the problem well with the developers. Next.js has an extremely poor DX regarding errors. I also expected it to tell me why the hydration failed and what element it thought was incorrect. In a real-world application, how am I supposed to find the incorrect nesting? I don't get any warnings in my local development at all. And after deploying the built version to my server, I got this vague error. Now I can't find what element is nested incorrectly. |
I think that is more a problem of React than Next.js, no? |
When the error is triggered by invalid DOM nesting it is almost rather trivial to figure it out though. And in many cases it is printed to the console. Granted there are cases where its hard to tell but then again, but I can hardly remember any. A tool like can i include can help out to validate DOM tag nesting as well. And yeah, Next.js is a React Framework for production, not React itself. |
I tried using tbody and thead...didnt work for me. Trying other methods now and making it from scratch. |
A funny thing about the error is that it appears when the page is translated in development. |
Is there any way to locate where the invalid nesting is present? |
It is impossible to find where the error is. Currently, we have to guess where it is. Sometimes it is super hard. |
I'm currently trying to fix this issue by deleting several files at a time from my project until I locate the culprit. There must be a better way to locate the issue? |
Is a lint or similar that can identify this issue? As others have pointed out, it's quite difficult without better tooling. |
Hey! To circle back here, this is a React specific error which logged a couple more lines in the development console. Next.js caught the first part of it and was not something we had control over. Notable changes:
OldNewToast: |
Would be great to improve this error reporting for Visual HTML Diff for
|
@maxcountryman in the meantime, before the error message is improved, there are some tooling things here:
|
it is impossible to fix this problem for me. |
Another update on a lint rule, cross-posting my comment from jsx-eslint/eslint-plugin-react#3310 (comment): Using some of the original (This has also been released as part of
/** @type {import('@typescript-eslint/utils').TSESLint.Linter.Config} */
const config = {
rules: {
'no-restricted-syntax': [
'warn',
// Warn on nesting <a> elements, <button> elements and framework <Link> components inside of each other
{
selector:
"JSXElement[openingElement.name.name='a'] > JSXElement[openingElement.name.name=/^(a|button|Link)$/]",
message:
'Invalid DOM Nesting: anchor elements cannot have anchor elements, button elements or Link components as children',
},
{
selector:
"JSXElement[openingElement.name.name='button'] > JSXElement[openingElement.name.name=/^(a|button|Link)$/]",
message:
'Invalid DOM Nesting: button elements cannot have anchor elements, button elements or Link components as children',
},
{
selector:
"JSXElement[openingElement.name.name='Link'] > JSXElement[openingElement.name.name=/^(a|button|Link)$/]",
message:
'Invalid DOM Nesting: Link components cannot have anchor elements, button elements or Link components as children',
},
// Warn on nesting of non-<li> elements inside of <ol> and <ul> elements
{
selector:
"JSXElement[openingElement.name.name=/^(ol|ul)$/] > JSXElement[openingElement.name.name!='li'][openingElement.name.name!=/^[A-Z]/]",
message:
'Invalid DOM Nesting: ol and ul elements cannot have non-li elements as children',
},
// Warn on nesting common invalid elements inside of <p> elements
{
selector:
"JSXElement[openingElement.name.name='p'] > JSXElement[openingElement.name.name=/^(div|h1|h2|h3|h4|h5|h6|hr|ol|p|table|ul)$/]",
message:
'Invalid DOM Nesting: p elements cannot have div, h1, h2, h3, h4, h5, h6, hr, ol, p, table or ul elements as children',
},
// Warn on nesting any invalid elements inside of <table> elements
{
selector:
"JSXElement[openingElement.name.name='table'] > JSXElement[openingElement.name.name!=/^(caption|colgroup|tbody|tfoot|thead)$/][openingElement.name.name!=/^[A-Z]/]",
message:
'Invalid DOM Nesting: table elements cannot have element which are not caption, colgroup, tbody, tfoot or thead elements as children',
},
// Warn on nesting any invalid elements inside of <tbody>, <thead> and <tfoot> elements
{
selector:
"JSXElement[openingElement.name.name=/(tbody|thead|tfoot)/] > JSXElement[openingElement.name.name!='tr'][openingElement.name.name!=/^[A-Z]/]",
message:
'Invalid DOM Nesting: tbody, thead and tfoot elements cannot have non-tr elements as children',
},
// Warn on nesting any invalid elements inside of <tr> elements
{
selector:
"JSXElement[openingElement.name.name='tr'] > JSXElement[openingElement.name.name!=/(th|td)/][openingElement.name.name!=/^[A-Z]/]",
message:
'Invalid DOM Nesting: tr elements cannot have elements which are not th or td elements as children',
},
},
};
module.exports = config; This handles the things we see that students most commonly need:
Looks like this on some invalid code: |
After all of this time, being out there helping out folks, and plain just learning more myself, I've updated the OP, and I feel good enough to close this issue. Do remember that this was opened mostly because of |
validateDOMNesting
error produces wrong HTML nodes in SSRvalidateDOMNesting
Hydration failed
This is going to help a ton. There are a couple of more rules that could be added, how can I contribute? |
Amazing, glad it can be helpful!
Three ways:
|
Having an invalid DOM structure, normally triggers
validateDOMNesting
, but when combined with SSR, this also triggersHydration failed
, andThere was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering
.I assume the above is a combination of two things, and error being raised by React, and Next.js not handling it well.
The React Error, as far as I know
React version: 17.0.2, 18.0.0 and 18.1.0.
The current behaviour
In SSR frameworks such as Next.js, an error raises claiming that
Hydration failed because the initial UI does not match what was rendered on the server.
.With React 18, and Next.js, this, in turn, triggers:
There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering
. I assume, Next.js is somehow not handling theinvalidDOMNesting
error, or theHydration failed
error, and that is probably something they ought to fix.Found this bit on the code as well:
Edit 2023
I've now gathered enough info and experience from helping out folks that run into this issue.
First the initial example I had provided is a weak case. Nested
<p>
tags are assumed by HTML interpreters as a mistake and render them sequentially instead. The react-dom/server rendering behaviour is correct. A structure such as :Is rendered by the browser as:
However, if you do this programmatically:
Then the DOM is not corrected by the browser and it renders:
Another case where the browser, or rather HTML interpreter I guess, changes the received server HTML:
The above is changed by the browser to:
However, when React does hydration it'll render a table without
tbody
, and that causes a mismatch with what it finds.Second, while it is annoying, a hydration error produced of invalid DOM nesting is one of the easiest to fix of its kind. The error log often points at where the divergence has occurred!
Third, in the face of
validateDOMNesting
errors, there are a few things that could help you figure out the error. I often follow this approach:Catch the browser making changes to the server sent HTML:
view-source:https://your-site.your-domain
, save this and take it to a text editoredit as text
the entire HTML document, uglify it and take it to a text editorAlthough the above could probably be automated somehow 😉
Are you making wrong assumptions about how HTML works? One resource I've used a lot to avoid this kind of issue is: https://caninclude.glitch.me/
And I think that's as far as you can go with
validateDOMNesting
kind of errors. There's other kind of errors, such as hydration mismatch because you straight up return different DOM, like:With that being said I am ready to close this issue.
The text was updated successfully, but these errors were encountered: