ReferenceError: localStorage is not defined in Next.js

Matthew C.

The Problem

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.

The Solution

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.

Use useEffect in a Client Component

To 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.

Lazy Load the Client Component and Disable SSR

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 Started With Sentry

Get actionable, code-level insights to resolve Next.js performance bottlenecks and errors.

Run the line of code below to:

  1. Create a free Sentry account

  2. Run the CLI install command to automatically add the Sentry SDK to your project:

    npx @sentry/wizard@latest -i nextjs
  3. 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.

Share on Twitter
Bookmark this page
Ask a questionJoin the discussion

Related Answers

A better experience for your users. An easier life for your developers.

    TwitterGitHubDribbbleLinkedinDiscord
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.