How to use Local Storage in Next.js
Tags: nextjs, react
Published: 2023-04-09 08:16:25

Have you received the following error while using local storage in Next.js?

{% embed https://youtu.be/lB-O5q6Pdp8 %}

```
ReferenceError: localStorage is not defined
```

In this article, we will see how to use local storage in Next.js

## Project set up

Create a Next.js application using the following command:

```bash
npx create-next-app next-local-storage
```

Update `index.js` with the following code:

```jsx
import { useState } from "react"

export default function Home() {
  let value
  // Get the value from local storage if it exists
  value = localStorage.getItem("favoriteNumber") || ""

  // Set the value received from the local storage to a local state
  const [favoriteNumber, setFavoriteNumber] = useState(value)

  // When user submits the form, save the favorite number to the local storage
  const saveToLocalStorage = e => {
    e.preventDefault()
    localStorage.setItem("favoriteNumber", favoriteNumber)
  }

  return (
    <div>
      <label htmlFor="number">Your favorite number</label>
      <form onSubmit={saveToLocalStorage}>
        <input
          id="number"
          value={favoriteNumber}
          onChange={e => setFavoriteNumber(e.target.value)}
        />
        <input type="submit" value="Save" />
      </form>
    </div>
  )
}
```

Now if you run the code, you will get the following error:

![ReferenceError: localStorage is not defined](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owrc037uppe44indvc8n.png)

## Why does the error occur?

The error occurs because the code gets compiled in the server (in localhost or server-side rendering in production), `localStorage` object does not exist.

## The fix

There are multiple ways in which it can be fixed.

### Checking if the window is defined

`localStorage` is an object inside the `window` object. The window object will not be defined in the server and hence the localStorage code will not be executed in the server.

```js
if (typeof window !== "undefined") {
  value = localStorage.getItem("favoriteNumber") || ""
}
```

### Using try-catch block

We can use a try-catch block to surround the `localStorage` access:

```js
try {
  value = localStorage.getItem("favoriteNumber") || ""
} catch (error) {}
```

### Using a useEffect hook

You can use a [useEffect hook](https://www.codingdeft.com/posts/react-useeffect-hook/) to get the localStorage content. The useEffect hook will not be run in the server and hence no possibility of the error.

```jsx
import { useEffect, useState } from "react"

export default function Home() {
  // Set the value received from the local storage to a local state
  const [favoriteNumber, setFavoriteNumber] = useState("")

  useEffect(() => {
    let value
    // Get the value from local storage if it exists
    value = localStorage.getItem("favoriteNumber") || ""
    setFavoriteNumber(value)
  }, [])

  // When user submits the form, save the favorite number to the local storage
  const saveToLocalStorage = e => {
    e.preventDefault()
    localStorage.setItem("favoriteNumber", favoriteNumber)
  }

  return (
    <div>
      <label htmlFor="number">Your favorite number</label>
      <form onSubmit={saveToLocalStorage}>
        <input
          id="number"
          value={favoriteNumber}
          onChange={e => setFavoriteNumber(e.target.value)}
        />
        <input type="submit" value="Save" />
      </form>
    </div>
  )
}
```

## Using a custom hook

You can use a custom hook to access local storage as well

```js
import { useState } from "react"

const useLocalStorage = (key, initialValue) => {
  const [state, setState] = useState(() => {
    // Initialize the state
    try {
      const value = window.localStorage.getItem(key)
      // Check if the local storage already has any values,
      // otherwise initialize it with the passed initialValue
      return value ? JSON.parse(value) : initialValue
    } catch (error) {
      console.log(error)
    }
  })

  const setValue = value => {
    try {
      // If the passed value is a callback function,
      //  then call it with the existing state.
      const valueToStore = value instanceof Function ? value(state) : value
      window.localStorage.setItem(key, JSON.stringify(valueToStore))
      setState(value)
    } catch (error) {
      console.log(error)
    }
  }

  return [state, setValue]
}

export default useLocalStorage
```

In `index.js`, you can use it as follows:

```jsx
import useLocalStorage from "@/hooks/useLocalStorage"
import { useState } from "react"

export default function Home() {
  // Get the value from local storage if it exists
  const [value, setValue] = useLocalStorage("favoriteNumber", "")

  // Set the value received from the local storage to a local state
  const [favoriteNumber, setFavoriteNumber] = useState(value)

  // When user submits the form, save the favorite number to the local storage
  const saveToLocalStorage = e => {
    e.preventDefault()
    setValue(favoriteNumber)
  }

  return (
    <div>
      <label htmlFor="number">Your favorite number</label>
      <form onSubmit={saveToLocalStorage}>
        <input
          id="number"
          value={favoriteNumber}
          onChange={e => setFavoriteNumber(e.target.value)}
        />
        <input type="submit" value="Save" />
      </form>
    </div>
  )
}
```


## Source code and Demo

You can view the complete [source code here](https://github.com/collegewap/next-local-storage) and a [demo here](https://next-local-storage.vercel.app/)