Why are document, window, localStorage and sessionStorage undefined in client components?
I'm having this function in a client component
But I'm getting an error saying
ReferenceError: localStorage is not defined.
Why is that? Isn't it supposed to work in client components?
The name "client component" is actually quite misleading. It's not a component that exclusively runs in the browser. It's a component that runs on the server and the client. The code inside the component is executed first on the server and the client or in other words, it's prerendered on the server and then hydrated on the client.
During that initial prerendering, since the component is rendered on the server, it doesn't have access to the browser APIs named above. Hence you got that error.
There are 2 fixes for this problem:
- move logic using browser-specific APIs to
useEffect(or an event handler, or any place that only runs on the client)
- use the component dynamically with
If we move the logic inside
useEffect hook, it will only run on the client. So, we can use these APIs without any problem.
Note that if you do it this way, as you can see you need a loading state, which may cause a flash of unstyled content (FOUC). To overcome it, you have to use a
next-themes which handles this problem very well, so you don't have to touch all this
By the way, there are already plenty of existing solutions made for many of these problems. For example, for
localStorage handling, you can have
If we import a component dynamically with
ssr: false, it will only run on the client, at the cost of the content not being available in the initial HTML (so crawlers may not be able to see it). If that is fine for you, you can proceed to use these APIs without any problem.