Have you received the following error while using local storage in Next.js?
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:
npx create-next-app next-local-storage
Update index.js
with the following code:
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:
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.
if (typeof window !== "undefined") {
value = localStorage.getItem("favoriteNumber") || ""
}
Using try-catch block
We can use a try-catch block to surround the localStorage
access:
try {
value = localStorage.getItem("favoriteNumber") || ""
} catch (error) {}
Using a useEffect hook
You can use a useEffect hook to get the localStorage content. The useEffect hook will not be run in the server and hence no possibility of the error.
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
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:
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 and a demo here
Top comments (14)
from next js 13 you must use
on top of the file
and it will work without any error and it's more cleaner solution
It will have an error in development mode since the component would still run on the server
does 'use client' works with nextjs pages? I'm a bit confused
Yes, it works on the new routes folder. Where all the components are server components by default. You need to specify
use client
To make it as a client side component
sorry I mean pages router not nextjs pages, is it an app router thing only?
This must be a joke.
How bad the framework needs to be if you can't use localstorage in 1 line of code.
How good framework needs to be if you can use local storage with a server side rendering.
I think conceptually its important to understand that you need to save the form data to local storage when the form field data changes during a client event (like submitting the form or making changes to form fields). With pure react you were able to update local storage using useEffect when the form object changes but this wont work in NextJS because the form object will clear (and hence nullify form fields in local storage) on initial component load before hydrating the client.
Takeaway approach (which is provided in this article): One useEffect to retrieve the local storage value if local storage is populated, and only write to local storage during a client event.
really helpful post!
Thanks for this. working code.
how to use Middle ware localstorage ?
Thank you for the useful hook, it works perfectly!
custom hook method is not working๐ฅฒ
source code: github.com/collegewap/next-local-s...
Demo: next-local-storage.vercel.app/
Hope it helps