I’m trying to store some basic (non-sensitive) user info about the client, such as their display name and profile picture, in react context. The information is stored in localstorage so that it persists even if the client closes/reloads the page, and the context fetches the data from there, where it’s displayed in a client component. Functionally, it works fine. However, I’m running into some problems:
-
In order to avoid a hydration mismatch error, I have to disable SSR for the whole component.
-
Even if I keep SSR enabled, the content takes much more time to hydrate than other client components, even if I build the app. This layout shift is really bugging me.
My question is, how can I make this info displayed immediately? I haven’t seen anyone else running into this problem. If I’m storing it the wrong way, what would be the right way?
I’ve created a minimum working example of the bug. I tried looking up all kinds of ways to use react context in nextjs, but none of them seem to suggest I’m doing anything wrong. I also tried to reverse-engineer next-themes to figure out how it works there, but I couldn’t quite understand it.
Any help would be greatly appreciated!
Update
I have tried to figure out how I can get rid of this delay, but to no avail. I have generally accepted that it’s just a consequence of using SSR, and server-rendered pages load even before client components can mount (though this time difference is less pronounced when in production mode). The way next-themes seems to avoid this is by simply preventing the page from being displayed until it detects the html tag and media query.
As a solution, I have implemented a skeleton component to avoid layout shift. I’m still open to solutions in case anyone knows how to make this information load instantly, but I have accepted that that might just not be possible.
Update 2
I have added an attempt to use Zustand instead context on my codesandbox linked above. It has the same issue – there’s still a brief period of time before the content loads. I want to know how users of Zustand counteract this problem. I still haven’t found any examples that have successfully solved this problem.
2
Answers
Your question seems to revolve around the
AuthContext
component and its interaction with server-side rendering in Next.js.The problem arises because the
userData
value differs between the server and the client, leading to a hydration failure. This failure occurs because React APIhydrateRoot()
expects the rendered content to match the server-rendered HTML.You can refer to the React documentation and the "pitfalls" section at the middle for more details. It’s advised to avoid using checks like
typeof window !== 'undefined'
or accessing browser-only APIs likelocalStorage
directly in your rendering logic, as it can cause mismatches between server and client snapshots.Here’s my workaround:
useState
hook with a default value ofnull
to ensure consistent initial rendering.useEffect
hook to update theuserData
after the component mounts.ClientComponent
.Here’s the modified
AuthContext
component:Note this approach might make hydration slower because your components have to render twice. Interfere the user experience on slow connections. The JavaScript code may load significantly later than the initial HTML render, so rendering a different UI immediately after hydration may also feel jarring to the user.
But again you shouldn’t access browser-only API in render logic.
Hope this useful to you.
you cannot do this if you store the user in localstorage because
localStorage
during the server rendering process is not possible.localStorage
is a browser API and does not exist on the server. In server-side rendering, next.js will generate HTML on the server and send it to the client. Hydration is the process where React attaches event listeners to the server-rendered HTML and makes it fully interactive. During this process, you should be assigning user asnull
, not authenticated. After hydration is completed, you can get the data from thelocalStorage
and update the user.If you want to show the user immediately, you should be handling authentication on the server.