wagmi: [bug] using WAGMI with SSR (Next JS) is causing styling issues
Is there an existing issue for this?
- I have searched the existing issues
Package Version
0.3.5
Current Behavior
(copying from a discussion we previously created)
After bumping wagmi to 0.3.5 in our project, we started to face a number of styling issues (here’s an example 😆) due to a mismatch between the server-rendered HTML and the first render on the client side.
After some investigation, I discovered that this was due to hooks like useAccount returning isLoading as true during SSR, but as false on the client. Here’s an example of the return value of useAccount during SSR and on the client:
Before we upgraded to 0.3, the SSR and client output was consistent on first render. In this case it returned:
[
{
"loading": false
},
null
]
A few questions:
- Why is loading true on the server side. In my tests, it’s also
truewhenautoConnectis set tofalse? - Is there a recommended pattern for handling SSR in
wagmi? Currently we’re manually patching this issue in many places, but I would prefer to help with a fix inwagmi😄
I would guess that anyone using Next.js + WAGMI + Stitches will face a similar issue to us.
Expected Behavior
No response
Steps To Reproduce
Styling bug isn’t visible in the repo to reproduce, but mis-match between client/server output is highlighted in a console error.
Link to Minimal Reproducible Example (CodeSandbox, StackBlitz, etc.)
https://github.com/smhutch/wagmi-sandbox/blob/main/README.md
Anything else?
No response
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 4
- Comments: 49 (23 by maintainers)
Commits related to this issue
- fix(pages/onboarding/): replace to useHybridAccount hook. Fixes issue related to https://github.com/wagmi-dev/wagmi/issues/542 — committed to rabani-to/interlace.community by D3Portillo 2 years ago
Hey!
Sorry, I completely missed the discussion thread, thanks for opening an issue here (definitely easier to keep track).
Yeah, there are a couple of nuances of server/client hydration in wagmi. The main culprit of these hydration issues is that wagmi now caches responses and persists them to local storage (which is obviously not accessible on the server). This is not just a wagmi issue, but also for any library that persists state to local/session storage. Currently, there is no first-class solution for SSR in wagmi (but this is something that is on the roadmap – perhaps using cookies).
There are a couple of workarounds to resolve these hydration issues right now that have their trade-offs:
useIsMountedHook and render your content after hydration. This can be seen in our examples. The trade-off here is content nested within theisMountedvariable is not visible on the server.isMountedvariable. The trade-off here is that it’s a bit annoying to haveisMountedvariables floating everywhere.3 (NOT RECOMMENDED). You can turn off local storage persistance completely, which will resolve everything, but comes with the trade-off that you lose data persistence on page load (and will consequently see FOUW – flash of unconnected wallet, balances, contract data, etc)
This is probably a bug on the wagmi side - I’ll take a look!
I think it’s okay to modify it like this.
Filling the codebase with the
isMountedis really annoying for me. And I think I found the workaround solution that just works.For anyone who is facing the “Hydration failed” problem, maybe you could give it a try.
I found that the
autoConnectfeature is the root cause, so I handle that myself. Ref: interal autoconnect functionAlso having this problem, I read that you wanted to explore this when next13 arrived, and since it is now in beta, I was wondering if you have an ETA on this feature? Kinda sucks to have to use workarounds in my apps, especially now since web3modal changed to the wagmi hooks in v2 (the previous hooks didn’t face this problem) and a lot of extra users will be facing these problems now.
Thanks in advance! @jxom
This is not fixed, the issue should be kept open
Hey @tmm 👋
Any chance we can get this re-opened? This issue isn’t fixed quite yet.
This is not a wagmi issue that needs fixing. You should follow SSR best practices for your site. Once the Next.js app directory is out of beta, we will look into adding a first-class integration in wagmi to make this easier.
An easy way to save developers from having to do
isMountedmanually from every callsite (i.e. “SSR hygiene”) could be to add a third non-boolean option forautoconnect, e.g.In the meantime, developers will probably have to put wrapper hooks around
useAccount, etc that returndisconnectedduring hydration(FWIW, autoconnect has always happened to be slow enough for me that I’ve not run into this issue)
For the isMounted fix, I found that neither the useIsMounted hook from usehooks-ts nor the useMountedState from react-use worked as both use a ref internally that is only set to
trueinside auseEffect, which runs after the rest of the content has finished rendering on the client. And since it’s a ref, updating it doesn’t trigger a rerender itself, so nothing shows up.To get it to work I had to write a simple
useIsMountedhook that usessetStateinstead so that a rerender is triggered:I was also not able to fix the hydration error with
"wagmi": "^0.6.6"using eitherisMountedorpersister: null.What is currently working for me is useEffect, like so:
then
Shout out to @ottodevs for this workaround. Hope that helps someone!
While the overall issue is “rendering a component that uses web storage to change its appearance fails hydration errors” which is not
wagmi-specific, the part ofwagmithat is causing the issue (caching data) I think could be extended to allow a new option for this situation:There’s already a
cacheTimeparameter for mostwagmihooks, but the documentation says it defaults to zero. So I presume the internal logic is that on initial call, it serves any stored data it has, then because the stored data is stale (past zero milliseconds old) fetches new, and replaces the stored data with the new value (possibly the same value, just new cache time). However I’m not seeing thestatusnorisLoading/isFetchingvalues update after page load…?A solution to this scenario could be a new parameter to specify always render with
nulldata once. Namely, if that parameter is set, the first time the hook is called, returndataofnull, with astatusofloading. Then look up to see if there’s cached data. If there is, updatedatato it (andstatusofsuccess), triggering a second rendering of the component.Most
wagmihooks haveenabledinput parameters, and specifyrefetchreturn values that I think could be used for another workaround style, but I don’t see documentation on them (or what the difference is betweenisLoadingandisFetching, and betweenisFetchedandisFetchedAfterMount); updated documentation and examples on those could possibly help developers code appropriately for this situation using those tools.Providing additional documentation on caching I pulled out into a separate request: https://github.com/wagmi-dev/wagmi/discussions/3017
this is nasty and i love it
FWIW I suspect that the inconsistent values on initial render is related to the usage of a global
configvariable, which will be gone inwagmi@2@apecollector – we can update the examples.
Update: Done. (https://github.com/wagmi-dev/wagmi/pull/1040)
@imornar first-class SSR support is on the roadmap, but we are holding until we see the Next.js Layouts RFC progress a little more (lots of folks use wagmi and Next.js together and we want to make sure wagmi’s SSR API design is compatible with the future of Next).
In the meantime, you’re welcome to take some of the ideas from https://github.com/wagmi-dev/wagmi/pull/689 — pass server-compatible storage to client and hydrate client state using
client.setState— if you are in need of an immediate solution.