-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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: Hydration mismatch error due to plugins generating script tag on top #24430
Comments
Might be related to (or same as) #22833, but let's keep both open for now |
Is the It does seem that to use |
From my understanding, if anything else other than document was passed in into hydrateRoot, it doesn't seem to crash when I have chrome extensions that modify the DOM installed (e.g. Dark Reader / Apollo DevTools). Here is the code sandbox: https://codesandbox.io/s/react-18-root-div-hydrateroot-1f5d5q?file=/src/Html.js:193-941 In the above example, I changed the following: Html.js export default function Html({ assets, children, title }) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="favicon.ico" />
<link rel="stylesheet" href={assets["main.css"]} />
<title>{title}</title>
</head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<b>Enable JavaScript to run this app.</b>`
}}
/>
+ <div id="root">{children}</div>
- {children}
<script
dangerouslySetInnerHTML={{
__html: `assetManifest = ${JSON.stringify(assets)};`
}}
/>
</body>
</html>
);
} App.js export default function App({ assets }) {
return (
<Html assets={assets} title="Hello">
+ <AppContent />
- <Suspense fallback={<Spinner />}>
- <ErrorBoundary FallbackComponent={Error}>
- <Content />
- </ErrorBoundary>
= </Suspense>
</Html>
);
}
+ export function AppContent() {
+ return (
+ <Suspense fallback={<Spinner />}>
+ <ErrorBoundary FallbackComponent={Error}>
+ <Content />
+ </ErrorBoundary>
+ </Suspense>
+ );
+ } index.js: - hydrateRoot(document <AppContent />);
+ hydrateRoot(document.getElementById("root"), <AppContent />);
Cypress was adding function $RC(a,b) {...} to the document. I'd assume it would crash if I hydrated the document. |
I don’t think the “stricter” behavior here is intentional. I’ll be checking with the team but my current understanding is that this is a bug. |
Something I've noticed is that when React does encounter a hydration mismatch, it attempts to fallback to client side rendering. Which does show up in the example codesandbox (the one where we are hydrating the document):
However, it results in an application crash because of the next error:
which is from the call stack: Then later another error is thrown:
which is from the call stack: What I am wondering about is: In the case of falling back to client side rendering, why does React do |
Yes, that issue is #22833. I believe the fix we wanted to do is changing which top-level element we modify. (Maybe body instead of html?) It would be nice to not have to introduce a separate “host config” method (which we’d have to do if we added a call to “replace”). So ideally the fix should use the methods we already use. |
Not an ideal solution but in my use case, I'm only concerned with generating/modifying the const Head: React.FC = () => {
if (globalThis.document?.head) {
return (
<head dangerouslySetInnerHTML={{ __html: document.head.innerHTML }} />
);
}
return (
<head>
{/* ... Do important things on the server */}
</head>
);
}; This is especially useful for me because even with current fixes added to |
I think I have the same error. But mine is from using styled-components. Initially, they put a style tag in the body, but then the style tag gets moved up to the head. You can check this repo here: https://github.com/adbutterfield/fast-refresh-express/tree/react-18 I tried just now using react@next/react-dom@next, but I still get the error. Of course, it's always possible that I'm doing something stupid in my code... |
I tried React 18.2.0, but it still breaks the page when Apollo Client DevTools extension is used. So my ugly solution for this problem is fix the DOM before document.querySelectorAll('html > script').forEach((s) => {
s.parentNode.removeChild(s);
});
const root = hydrateRoot(
document,
<React.StrictMode>
<AppWithFullDocument />
</React.StrictMode>,
); Note: You can replace the selector more strict query .. e.g. |
Getting the issue with Cypress tests, as suggested in Remix Discord, my workaround for those coming here: if (process.env.NODE_ENV === 'test') {
require('react-dom').hydrate(<RemixBrowser />, document);
} else {
hydrateRoot(document, <RemixBrowser />);
} 🙏 for a fix soon, thx all! |
@dbashford Which in particular issue are you hitting? We’ve released a short-term fix for the most urgent part of the problem in 18.2 (document fallback didn’t work at all). Now it should work by doing a full client render but this will lose the third party things. There is a possible fix to do something smarter but it’s a bigger project and a bit further on the timeline. So it would help to know what exactly broke for you. Is there a repro? Particular scenario? Thanks. |
Mine is the Cypress case. Everything works fine in dev and in prod, but Cypress when it kicks up fails (418), and only after making the |
Hey @Mordred thanks a lot for this quick hack, I modified it slightly to also get rid of document.querySelectorAll("html > script, html > input").forEach((s) => {
s.parentNode?.removeChild(s);
});
|
Nevermind this, was local NODE_ENV issue (cypress issue above is still a problem, though)
|
@dbashford Can you share a minimal project? It always helps to have concrete examples to check fixes against. This goes for everyone else too. |
@gaearon Can call off the dogs on that latest comment, was something I introduced myself while trying to debug a real hydration issue with dates. The Cypress issue is still a problem, that's all local and consistent. I'll work to get a repro up over the next few weeks when I get a second. |
I just updated my repo with some instructions to more easily reproduce the issue, and test that it works when not using styled components. See here: https://github.com/adbutterfield/fast-refresh-express/tree/react-18 |
Hi @gaearon I have a repro repo for the hydration errors with cypress. It's not exactly "minimal", it's a freshly-created Remix grunge stack app, which has quite a bit of stuff in it. Here it is: https://github.com/camjackson/remix-cypress-error. Here are the steps to repro the error locally:
The test will fail with a hydration error. To show that it's related to react 18, you can open up To debug it further, instead of Oh and one final note, if you just start the app normally with For completeness, here's how I created this repo from scratch:
|
I have replicated same behaviour - it can be replicated anywhere with
|
Hey everyone 👋🏽 I am currently facing the same issue on my project. I have created the following reproduction repo , in my case I use Remix + Vite + Tailwind. But the hydration issues have been around long before the Vite integration. It was only with the introduction of Vite, that this was most noticeable because, on Save, and after the HMR kicks in you will see the styles being removed from the DOM. The following solves my issue:
And this fixes my styling issues despite continuing to get hydration erros in the console.
Knowing now what I know, the root cause might be |
my hydration error got fixed with:
For testing i added back the cloudflare email obfuscated script injection + enable back the chrome extensions that triggers the hydration issue for me, Apollo client dev tools / Requestly and have not seen the issue 🙌 |
I cant believe this is still a problem for the past 2 years. I have LastPass installed, and even a Hello World app with ONE input tag type "text" and a placeholder "email" is causing the hydration issue. and no, suppressHydrationWarning does not fix the problem. (placed it everywhere without luck) :( |
I'm getting the same error and I'm looking for a solution. |
injected elements with style attribute throw an ssr mismatch error... why is this not fixed yet... |
With all due respect @Omar-Abdul-Azeez, but it's an OS project.. |
Can confirm LastPass browser extension causing the hydration mismatch. |
Can you please tell me if this happens on 18.3.1 and on canary? |
Seems react 19 (beta) might fix this, for me at least. Renders nicely with browser addons without errors |
Can confirm the same. Lastpass browser extension was causing error. |
Lastpass issue confirmed here as well. Took me a while to figure it out and ended up here.. |
Tried this, unfortunately didn't work for me. Confirmed still an issue with LastPass browser extension causing it. |
Still seems to be an issue when testing with Remix and React 18.3.1. It happens when the browser tab has been inactive for some time and it is focused again. |
same happening with extension "I still don't care about cookies". |
Can confirm that all our issues introduced by upgrading to React 18 were solved by upgrading to React 19 🎉️ (currently |
I still get the warning with LastPass on the Oct 29 2024 React 19 and React-Dom 19 versions 19.0.0-rc-603e6108-20241029...but I love the new messaging. It shows git-style "minus" signs to show the difference between the server and client renderings, not to mention way better explanations and formatting. I think it is worth showing a screenshot for those who don't have this level of detail in React 18. Kudos to developers to analyze the issue to this level of detail. UPDATE: I submitted this issue in the LastPass community. Not that it will go anywhere...perhaps React can still do something about working with plugins (?) |
if lastpass adds the tag suppressHydrationWarning I believe that the error could go away. At least that's how jam chrome extension works |
React version: 18.0.0, 18.1.0-next-fc47cb1b6-20220404 (latest version in codesandbox)
Steps To Reproduce
Link to code example: https://codesandbox.io/s/kind-sammet-j56ro?file=/src/App.js
The current behavior
If a script tag is inserted before the head tag due to the user's browser environment such as a plugin, it is judged as a hydration mismatch and the screen is broken.
2022-04-24.11.02.00.mov
The expected behavior
This problem may be a part that each third party needs to solve, but I'm wondering if it's possible to handle an exception in the hydration matching logic of React.
The text was updated successfully, but these errors were encountered: