Cause and Fix for localStorage.getItem is not a function Error in Node.js 25 + Next.js 15
Introduction
When running npm run dev in a Next.js 15 project, the following error occurred and the development server stopped working properly.
There was no code directly calling localStorage, which made identifying the cause time-consuming. This article explains the root cause and solution for this error.
Environment
- Node.js: v25.2.1
- Next.js: 15.3.8
- next-intl: 4.3.5
- OS: macOS (Darwin 25.2.0)
Root Cause
This error occurs with the combination of the Web Storage API introduced in Node.js 25 and Next.js 15.
History of localStorage in Node.js
Node.js has been experimentally implementing the Web Storage API (localStorage / sessionStorage) since v22.
| Version | localStorage Behavior |
|---|---|
| Node.js 21 and earlier | globalThis.localStorage is undefined |
| Node.js 22-24 | Only active when --localstorage-file flag is specified (experimental feature) |
| Node.js 25.0.0 | Web Storage API enabled by default |
In Node.js 25, globalThis.localStorage object exists by default. However, without specifying a valid file path with the --localstorage-file flag, the localStorage object itself exists but methods (getItem, setItem, etc.) do not function – a half-working state.
Specifically, as reported in Node.js Issue #60303, there was a problem where localStorage became “an empty proxy object that returns undefined for all property accesses.”
Why It Becomes a Problem in Next.js 15
Many libraries guard against server-side localStorage access like this:
In Node.js 24 and earlier, globalThis.localStorage was undefined, so this guard worked correctly. In Node.js 25, however, the localStorage object exists, so the guard is bypassed. When getItem is called, the method is undefined, resulting in TypeError: localStorage.getItem is not a function.
Some of Next.js 15’s internal code and dependency libraries match this pattern, causing errors during server-side rendering.
Solutions
There are two main approaches to this problem.
Method A: Downgrade Node.js Version
Downgrading Node.js to v24 or below will restore globalThis.localStorage to undefined, resolving the issue. However, this is a temporary workaround.
Method B: Upgrade to Next.js 16 (Recommended)
This issue has been fixed in Next.js 16 (vercel/next-learn#1129). We adopted this approach for our project.
Next.js 15 to 16 Upgrade Procedure
1. Update Packages
Updated versions:
| Package | Before | After |
|---|---|---|
| next | 15.3.8 | 16.1.6 |
| next-intl | 4.3.5 | 4.8.2 |
| react | 19.1.1 | 19.2.4 |
| react-dom | 19.1.1 | 19.2.4 |
| eslint-config-next | 15.3.3 | 16.1.6 |
2. Rename middleware.ts to proxy.ts
One of the major breaking changes in Next.js 16 is the rename from Middleware to Proxy.
The file contents do not need to change. middleware.ts remains as deprecated but is scheduled for removal in future versions.
3. Change the lint Script
In Next.js 16, the next lint command has been removed. Change to calling eslint directly.
4. Update ESLint Configuration
eslint-config-next@16 now exports native flat config. The FlatCompat wrapper from @eslint/eslintrc is no longer needed.
Before:
After:
5. next.config.ts
Custom webpack settings (WASM / ONNX support) are kept as-is. In Next.js 16, Turbopack becomes the default bundler, but webpack settings can still be used with next build --webpack. In our project, Turbopack built without issues, so no changes were needed.
6. Areas That Required No Changes
params/searchParams: Already migrated to async access usingawait- API routes: Using
NextRequest.nextUrl.searchParams, no changes needed - Client components:
useParams()/useSearchParams()hooks require no changes - i18n settings (
routing.ts,request.ts): No changes needed
Verification Results
| Check Item | Result |
|---|---|
npm run typecheck | Pass |
npm run lint | Pass (only 1 existing warning) |
npm run build | Turbopack build successful |
npm run dev | localStorage error resolved |
Summary
- Root cause: Web Storage API was enabled by default in Node.js 25, causing the
localStorageobject to exist but methods to not function without--localstorage-filesetting. Next.js 15’s internal code could not handle this “broken localStorage” - Solution: Resolved by upgrading to Next.js 16. Downgrading Node.js is also a viable workaround
- Upgrade notes: Renaming
middleware.tstoproxy.ts, removal ofnext lint, and native flat config support for ESLint configuration are required
The addition of Web APIs accompanying Node.js version upgrades can have unexpected impacts on server-side frameworks. In particular, it is important to recognize that implementations relying on typeof guards can break with the addition of new APIs.