Gatsby, React & Hydration #17914
Replies: 45 comments 7 replies
-
Thank you for the detailed issue, @eyalroth I think we should do two things here
Would you like to do this considering you've done all this great work in investigating this? 🤗
|
Beta Was this translation helpful? Give feedback.
-
I feel like this should be mentioned in multiple locations, not just in the debugging section, which I believe people tend to get to very late in their development and/or debugging process (and some might not even notice the problems at first). Of the locations I thought would be appropriate to mention this:
Perhaps this problem deserves a dedicated page / blog post?
I actually mentioned the problem in part of my guide on how to implement a layout in a Gatsby site. It's in my blog, which is not published yet, and will likely go through several more edits. The post is currently online here, where's the source for it is here. I'm not sure it follows the official style guide though 😆 |
Beta Was this translation helpful? Give feedback.
-
I'm quite confused by the last few replies around this so broadly mentioned issue. Thank you for suggesting these workarounds @eyalroth! I'm actually already testing the hydration-one. However, as you mentioned yourself, this might have a performance impact and in general, is still a "workaround". However, I really appreciate your efforts and will get back to you when I have more feedback from my experiments. @sidharthachatterjee wouldn't it be better if we come up with a reasonable and grounded explanation of why this issue is happening in the first place? Instead of thinking about how to document a workaround which is still unverified? Did anyone confirm that these proposed solutions fixed the initial issue? What about Does this mean we're accepting a potential performance hit? Is this the solution we're accepting? Just trying to understand what's next here? |
Beta Was this translation helpful? Give feedback.
-
I have been struggling with this bug for a very long time. Happy to see that it is finally getting some attention. 👍 I have recently run in it when I was using Also, I have noticed that this behavior varies when I load the page for the first time. Probably because of the One more issue that may be related to this bug: #15993 |
Beta Was this translation helpful? Give feedback.
-
Multiple layouts are actually quite a complicated feature, and may not work hand in hand with the layout plugin. I've written about it here (note that this is still unpublished and may be subjected to edits). |
Beta Was this translation helpful? Give feedback.
-
This is great research and an awesome writeup! Thanks a bunch @eyalroth 🙂 I'm interested in other's thoughts on this too, since I'm not entirely sure what the nature of a guide like this would look like, maybe a write up similar to what you've done in this issue. |
Beta Was this translation helpful? Give feedback.
-
Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing! Thanks for being a part of the Gatsby community! 💪💜 |
Beta Was this translation helpful? Give feedback.
-
It seems to me that at least a subset of these 'mysterious' issues where I've had a similar issue that was caused by variables imported from an .scss stylesheet: import vars from './layout.scss' The imported variables are undefined on @eyalroth @sidharthachatterjee @SMerdzhanov @LekoArts Can anyone confirm if this is a gatsby issue or a configuration issue? |
Beta Was this translation helpful? Give feedback.
-
@eyalroth @sidharthachatterjee @SMerdzhanov @LekoArts For your convenience, I filed #19563 to track this preprocessor issue. It includes a minimal reproduction that clearly demonstrates this problem: |
Beta Was this translation helpful? Give feedback.
-
@DavidDeprost Thank you for filing that issue. I think it's more related to #10706, and not so much to this ticket. The problem addressed here seems to be rooted in a behavior which affects both the production and development builds. |
Beta Was this translation helpful? Give feedback.
-
You're totally right, it is more related to that issue. I only came to this issue because it was mentioned in some of the other issues that they were closed in favor of this one. I guess I had the mistaken impression this was also the place to gather other possible causes. Thanks for the issue number, the preprocessor issue will be of more use in there. |
Beta Was this translation helpful? Give feedback.
-
Thank you for keeping this thread up 👍 Both issues linked #19563 and #10706 seems related, but not quite actionable/active. I'm curious how there aren't more people experiencing the same issue. In any case, for the moment I've managed to apply a workaround that seems to be a fix for now. However it's a workaround and has some negative performance impact. I'm talking about replacing the So far I haven't found any better solution. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I've run into this issue and was about to pull my hair out. The only solution (janky) I've found was here. This is obviously less than ideal but I'm not sure how to help debug this further. |
Beta Was this translation helpful? Give feedback.
-
This is a constant issue in my experience. It constantly pops up and is a total pain. This happened while using Formik |
Beta Was this translation helpful? Give feedback.
-
@blainekasten This is not a develop-only problem, it's affecting production as well. |
Beta Was this translation helpful? Give feedback.
-
Hey @blainekasten, I'm a bit confused maybe. Issue #25729 is about running true SSR in If I understand correctly, that's an attempt to be able to see and eventually prevent the issue from happening by pointing it out while developing. But this isn't actually going to solve it, right? So then #25729 would be helpful but it seems not like a solution but patch? Don't we want to keep this thread open and actually resolve the issue? |
Beta Was this translation helpful? Give feedback.
-
If I'm not mistaken, the fact that SSR & client mismatches mess up the DOM is not a Gatsby problem it's a React problem, and it's expected to happen. It doesn't matter if we are in development or production mode, these issues in your code must be fixed before shipping to production. If we have SSR in development, we will be able to fix those issues and there shouldn't be mismatches in production. In my opinion I don't think there is anything to "fix" in Gatsby, except being able to see those React warnings when developing. It's the same as missing |
Beta Was this translation helpful? Give feedback.
-
@verekia There are no fixes, only partial workarounds. This might be a problem stemming from React, but dismissing it as a non-Gatsby problem for this reason is akin to dismissing a food poisoning in your restaurant due to bad handling by the food supplier. At the end of the day, no one will come eat at your restaurant. This issue might have been dismissed if it didn't affect anyone, or affected only a niche use-case, but this is one of the highest voted issues in this repository. This is no surprising, because it is a big problem with one of the most fundamental features / characteristics that Gatsby is aiming to provide -- fast (static) yet flexible (dynamic) web pages. |
Beta Was this translation helpful? Give feedback.
-
While it won't affect many users, those who use art direction feature of React hydrates to the SSR state assuming it's the same instead of re-rendering if not, but due to the nature of that problem, you'd also have breakage prior to JS being available, so that needed to be addressed to. I created a PR that resolves all that, the hydration issue is a pretty simple fix toggling a bool state as the official React docs advise. I'd prefer the benefits of Developers can get an SSR update via |
Beta Was this translation helpful? Give feedback.
-
Well if they do implement SSR in development mode, it's pretty easy to put it behind a flag.
@polarathene That's the point, this is currently not possible because warnings are not shown in production mode. The app is just completely messed up and there is no information about what's happening. "Why does my header look like a button?! Why is my title sticking at the bottom of the screen?!" It is extremely confusing. And even if you are aware of hydration problems, it's hard to debug. It would be a bit better if there was a way to show warnings in production mode, but implementing SSR in dev provides a much better workflow and lets developers try their app with full debugging capabilities. |
Beta Was this translation helpful? Give feedback.
-
@verekia I agree that the development mode should resemble production as much as possible, but the development mode has other workflow features in mind, such as reloading changes on the fly. If those are incompatible (I can't say as I'm not proficient enough in this), then the choice is between two separate workflows. This is outside of the scope of this issue, and in fact, this is addressed by this issue, which is also linked in the description of this one. Regardless, even if development mode was identical to production, I believe this issue would've plagued many projects. It is hard to detect for other reasons as well, and in general it is not documented enough -- in fact, some official guides of certain plugins contain instructions that will raise this problem. IMO gatsby should aim to provide plugins and other solutions that make it easy to avoid this problem, and that it should only be encountered in niche use cases, which doesn't seem to be current state of affairs. |
Beta Was this translation helpful? Give feedback.
-
If you are just looking to copy-paste the two-pass rendering solution, here's the HOC version: export const withTwoPassRendering = (WrappedComponent) => ({
children,
...rest
}) => {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, [setIsClient]);
return (
<WrappedComponent {...rest} key={isClient}>
{children}
</WrappedComponent>
);
}; And the usage example: Before: <MyComponent prop={value}>
...children
</MyComponent> After: const MyComponentWithTwoPass = withTwoPassRendering(MyComponent);
// ...
<MyComponentWithTwoPass prop={value}>
...children
</MyComponentWithTwoPass> |
Beta Was this translation helpful? Give feedback.
-
Man the fact that this has been a known issue for over a year without any substantial update on how to resolve this, scares the shit out of me. I'm having to rely on the workaround of skipping rehydration and its murdering the performance of my production app. At this point I'm about to move my code base from Gatsby to Next. Am I over reacting here? Is the gatsby team working on a fix and not a patch? Has anyone moved their code base to NextJs for this reason already? |
Beta Was this translation helpful? Give feedback.
-
I found this article very helpful in working around this problem: https://www.joshwcomeau.com/react/the-perils-of-rehydration/ |
Beta Was this translation helpful? Give feedback.
-
After countless hours, I discovered why styles were broken after SSR hydration on production builds. The problem was that babel-plugin-styled-components doesn't work correctly if styled-components is re-exported (styled-components/babel-plugin-styled-components#351). import styled from '../lib/styled-components'; // Does not work :(
import styled from 'styled-components'; // Works If you were re-exporting the module to add theme type definitions, please use module augmentation instead. // src/types/styled-components.ts
import type { Theme } from '@material-ui/core';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {
}
} |
Beta Was this translation helpful? Give feedback.
-
Has this been fixed? Tired of using work-arounds. |
Beta Was this translation helpful? Give feedback.
-
Any update in this? Currently using https://www.joshwcomeau.com/react/the-perils-of-rehydration/, but even using simple dynamic class template strings causes my whole page to be rendered twice? Very strange and difficult to debug. |
Beta Was this translation helpful? Give feedback.
-
Ok, writing this to future myself: removing the "native" image Even though @polarathene got some presumably related fix merged in, it doesn't work for me on the latest Stumbled upon this more than once, unfortunately. |
Beta Was this translation helpful? Give feedback.
-
Why is this still an issue ?? Jesus.. |
Beta Was this translation helpful? Give feedback.
-
Seems that hydration-related issues are extremely common when developing in GatsbyJS, but the issue has not been fully and formally addressed, so I would like to do so here.
The problem
Due to many Gatsby sites having some dynamic components -- despite being mostly statically generated -- and due to the SSR model behind Gatsby, many Gatsby sites fails to properly update HTML attributes; most notably
class
, but alsohref
and so forth. This happens when the client and server DOMs differ in their attributes, as the React hydration functionality does not (re)consolidate them as well as the render method:To give a few instances of this problem from this repository, here is a short list: #3741 #5100 #8560 #9911 #10370 #12360 #14601 #17676 .
It is not surprising that the problem is so common. It is often hard to detect and resolve, and for two main reasons:
Reproduction
I also created a minimal Gatsby site repository to reproduce the problem: https://github.com/eyalroth/gatsby-hydrate-bug
The site has only 2 pages and a layout. The layout is automatically added to the 2 pages via
wrapPageElement
(same code as ingatsby-plugin-layout
). The layout wraps the page content with adiv
that has aclass
attribute set to the current time, while also appending the time as text beneath the page content.Upon building (and serving) the site, navigating to the index and inspecting it in the developer tools, one can see that the time displayed in the page is not the same as in the div class attribute:
Navigating to the second page will correct this behavior, and you'll see that the time will be the same between the page content and the
class
attribute:This will remain so as long as you keep navigating between the pages in the same window. If you happen to refresh the page or open it in a window, the inconsistency will creep back; you'll actually notice that the time in the
class
attribute will remain the same every time you refresh (hinting that it is cached). "Hard refresh" (CTRL + F5) will load the page properly.Workarounds
Replacing hydration with rendering
One possible solution to (some instances of) the problem is to entirely replace the hydration with rendering:
However, there are two major drawbacks to this workaround:
Two pass rendering
The other workaround is to re-render a component which is expected to change between the client and the server when it first mounts, and attach a different
key
attribute to the component the second time around:This workaround is also documented in React's website:
The drawback of this approach is that the user may very well visually notice the re-rendering, which wouldn't be the best user experience.
What's next?
I believe this problem should be better highlighted and documented in the Gatsby site. As I said earlier, it is hard to detect and be aware of, especially when (new) developers -are not always aware of the entire design behind the framework, and may very well not be able to tell what's the differences between server-side or client-side rendering, and when the components are expected to differ between the two.
This is most notable with
gatsby-plugin-offline
, where (I believe) the problem is happening since the default shell page is first generated with all the components inwrapPageElement
(but without any page content), so only these components are actually affected by the problem; for instance, a layout component "injected" bygatsby-plugin-layout
.#17676 is reporting that the problem occurs even when the offline plugin is disabled, so I'm not sure whether a builtin workaround in that plugin -- for instance, generating the default shell app without
wrapPageElement
-- will be enough to solve the problem for all the various use-cases.Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions