NextJS - Access window and localStorage
Tags: nextjs, javascript, node
Published: 2022-12-02 20:16:40

Next.js is a React framework with pre-rendering abilities. This means that for every page, Next.js will try to generate the HTML of the page for better SEO and performance.

`Window` and `document` are unavailable on the server. So you'll run into errors such as `ReferenceError: window is not defined` if you are trying to access `document` or `window` properties such as `local storage`.

![NextJS - Access window and document and localStorage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wnlm3wxm1r5cgxotzw8m.png)

To avoid these undefined errors at compile and build time, you have different options.

## A.1 - Run Only for browser users
You can run a simple check, and only if a user is a browser user run your code. This way server will not take your code into consideration.
Since `process.browser` is deprecated you must use `typeof window`.

```js
if (typeof window !== "undefined") {
  // Write your client-side statements here.
  window.localStorage.getItem("key");
  window.localStorage.setItem("key", "value");
}
```
**Note:** this is good way if you want to do simple operations and other variables in other components do not depend on this value or you will get error such as `"can not read the property of undefined"`. If your code do so or you have many similar operation in your code, Look for solution "**A.2"**.

---


## A.2 - Actual window & dummy window (Recommended):
**"A.1"** is good and it'll work, but you need to explicitly check if it is a client-side rendered component or server-side every time.

To avoid this issue you need to create an mock `window` object.
then use mock `window` for server-side logic and actual `window` for client-side.

```js
let WINDOW = {};

if (typeof window !== "undefined") {
  // When code is on client-side. So we need to use actual methods and data.
  WINDOW = window;
} else {
  // When code is on server-side.

  // Other component are mostly server-side and need to match their logic and check their variable with other server-side components and logics.
  // So following code will be use for them to pass the logic checking.
  WINDOW = {
    document: {
      location: {},
    },
    localStorage: {
      getItem :() => {},
      setItem :() => {}
    },
  };
}

export default WINDOW;
```
**Note that:** Depending on what property of `window`, `document`,... or what methods of those property you have used, your implementation details will be different.

Now use `WINDOW` instead of `window` in your code:

```js
  WINDOW.localStorage.getItem("key");
  WINDOW.localStorage.setItem("key", "value");
```

---


## Example of use:
I created a custom hook for using `local storage`:

```js
export const useLocalStorage = (key) => {
  // returns value related to initial key.
  const item = WINDOW.localStorage.getItem(key);
  // return an function that will save given value to initial key
  const setItem = (value) => WINDOW.localStorage.setItem(key, value);
  return [item, setItem];
};
```

---



## Warning:
remember, **never** create a function for window type checking.
it always **must be an explicit type checking** or it will not work

**Wrong** way:

```js
const hasWindow = () => {
    return typeof window !== "undefined"
}

if (hasWindow()) {
    // client-side operation such as local storage.
    localStorage.setItem(key, value)
}

if (!hasWindow()) {
    // server-side code
}
```
read more and reason for this problem: [NextJS GitHub issues page](https://github.com/vercel/next.js/issues/5354#issuecomment-649800433)

---

## B - EventListener and useEffect hook
If you are simply registering `EventListener` such as a "Scroll" event to your `window` object there is no need to go through all these troubles.
The `React` way to solve this issue would be to use the `useEffect` hook. Which only runs at the rendering phase, so it won't run on the server.

```js
useEffect(() => {
  window.addEventListener("scroll", () => { console.log("scroll!"); });
}, []); // empty dependencies array means "run this once on first mount"
```
**Tip:** The way you should use `useEffect` is to register and unregister the listeners on "mount"/"unmount". But you could also just register on "mount" and ignore any other rendering event, like what we did in the example.

**Note: Make JavaScript logic Independent to components**
`useEffect` is a hook and it must be used in functional components.
But at the same time, it's wrong for our JS codes that are unrelated to a specific component to depend on them.
Read more and solve this issue at:
[Convert and Execute JS code as React Component](https://dev.to/ariyou2000/convert-and-execute-js-code-as-react-component-4m1j)


---

## C - No Server Side Rendering
You can make it so your component won't even be rendered on the `server-side` at all.

**This solution works particularly well when you're importing external modules depending on `window`.**
But it is not recommended to use unless you really know what you are doing and using it at the right place. Because misuse of it makes your next project meaningless (You are not taking advantage of a core NextJS feature (`SSR`)).

[Next.JS - Dynamic Import](https://nextjs.org/docs/advanced-features/dynamic-import)

---

40% of this post is thanks to these guys and a copy-paste of similar post written by them:
[Ibrahim Adeniyi](https://dev.to/dendekky)
[Vincent Voyer](https://dev.to/vvo)