Sentry Answers>Next.js>

ReferenceError: localStorage is not defined in Next.js

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:

Click to Copy
Error: window is not defined

You may have encountered the following error if using the localStorage property:

Click to Copy
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:

Click to Copy
"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.

  • Community SeriesDebug Next.js with Sentry
  • ResourcesJavaScript Frontend Error Monitoring 101
  • ResourcesSentry vs. Crashlytics: The Mobile Developer's Decision-Making Guide
  • Syntax.fmListen to the Syntax Podcast
  • Syntax.fm logo
    Listen to the Syntax Podcast

    Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.

    SEE EPISODES

Considered “not bad” by 4 million developers and more than 100,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.

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