In Next.js version 13+, you may have encountered the following error when trying to use the localStorage
browser API if you used the window.localStorage
property:
Error: window is not defined
You may have encountered the following error if using the localStorage
property:
Error: localStorage is not defined
In a browser environment, windows.localStorage
is the same as localStorage
, and localStorage
is a property of the global window
object.
This error occurs because, from Next.js version 13, components are server-rendered by default. Components are pre-rendered into HTML on the server before being sent to the client. The window
object is not available on the server.
We need to make sure that the code to get values from local storage and save values to local storage only runs on the client where the window
object is defined. We can do this by using the useEffect
hook in a Next.js client component or by lazy loading the component.
To use browser APIs like the window
object, create a client component by adding the React "use client"
directive at the top of your component file, above your imports. Client components are pre-rendered on the server, like server components, for a fast initial payload.
The client component is then made interactive by adding event listeners to the static HTML. This process is called hydration. Client components render on the server and the client.
useEffect
in a Client ComponentTo make sure that the code using the window
object is run on the client only, add it to a useEffect
hook.
Below is an example counter component that stores a count state variable in local storage:
"use client"; import { useState, useEffect } from "react"; export default function Counter() { const [count, setCount] = useState(null); useEffect(() => { const savedValue = window.localStorage.getItem("count"); setCount(savedValue ? Number(savedValue) : 0); }, []); useEffect(() => { if (typeof count === "number") { window.localStorage.setItem("count", count); } }, [count]); return ( <button onClick={() => setCount(count + 1)}> Count: {typeof count === "number" ? count : <span>...</span>} </button> ); }
In this component, a button is rendered that increments the count
state variable by 1 when it’s clicked. The first useEffect
hook gets the value for count
from local storage or sets it to 0 if it has not been added to local storage yet. The second useEffect
saves the value of the count
variable to state when the useEffect’s dependency, count
, changes. This happens when the button is clicked.
Alternatively, you can lazy load your client component and disable pre-rendering on the server so that it’s only rendered on the client. This may increase the time taken for the page to initially load. This approach is useful when using a client-only library or when you want to defer loading the component until a specific action happens, such as the user clicking a button.
Get actionable, code-level insights to resolve Next.js performance bottlenecks and errors.
Run the line of code below to:
Create a free Sentry account
Run the CLI install command to automatically add the Sentry SDK to your project:
npx @sentry/wizard@latest -i nextjs
Start capturing errors and performance issues
Loved by over 4 million developers and more than 90,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.