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.

}[(Tn[dyoTip[dygeTepeEy:esrp2Etre4r:oE0rrr5o':r)r2o:8lrW9o:al2cro7alnc0loia3Scnl8tagS7ol:t9rSo'at`rgo-aer-g.aleggo.eecgt.aeIglttesIetttmIoetrmieasmgiesni-osfntionltaoet`afuawfnaucfsntucinptocrintoo]ivnoi]nd]e{d{wpiatgheo:ut/ajav'al}idpath

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.

VersionlocalStorage Behavior
Node.js 21 and earlierglobalThis.localStorage is undefined
Node.js 22-24Only active when --localstorage-file flag is specified (experimental feature)
Node.js 25.0.0Web 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:

i}fcC(ootnmysmptoenovfagluluaoercda=lpSlatotoctraealrgSneto!r=a=ge'.ugnedteIftienme(d''k)ey{');

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.

E12345r.....roNIIlgrennoextctfteNaIl.roltojndSewsaetm:l.o1jri5csasogrd2euee5.nn,gddoeeerltfroIisdctneaeepplmdaeS(gnt)edoeriTwnasyicgptyecehaElolrSiblrSbjeoRredracrtoyceccxuhiressctksstygpueaorfdlpoacsasleSstorage!=='undefined'

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.

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

npmesilnisntta-lclonnfeixgt-@nleaxtte@sltatneesxtt-@ienstlli@nlta/teessltinrteracc@tl@altaetsetstreact-dom@latest\

Updated versions:

PackageBeforeAfter
next15.3.816.1.6
next-intl4.3.54.8.2
react19.1.119.2.4
react-dom19.1.119.2.4
eslint-config-next15.3.316.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.

mvsrc/middleware.tssrc/proxy.ts

The file contents do not need to change. middleware.ts remains as deprecated but is scheduled for removal in future versions.

iie}e}mmx)x;pppl;pm]soooooarrrr.crtctttratc/olhpc{duece(rretPor?oerfirn:!xaoanes_ytuugft[n.etl,ietMitxcxsin:otdgcn|(dr'facl}eaipoeasginwft-|taren=feroMeanemie{vtddisfdecrldoio/e'ndmiw.e1ain'8rctnneoie/(|cxr{matool-uditetnilotnslg|m/'cim;odinddfldielgwe|aw.ra*er\.e\t'.s;.)*|ort.wasm|.*\\.onnx).*)'

3. Change the lint Script

In Next.js 16, the next lint command has been removed. Change to calling eslint directly.

{}"}sc"rliipntts""::"{eslint."

4. Update ESLint Configuration

eslint-config-next@16 now exports native flat config. The FlatCompat wrapper from @eslint/eslintrc is no longer needed.

Before:

iiiccc}c]emmmooo)o;xpppnnnb;n{}{}pooosssas,,orrrtttst.ir}rtttecgu,tcDeonl"{{{__oismoe@dfdmrlprsnedfFiipeiae:efiillracntsxarlaenttt.:{tunetnaoCe/laUCam=rox[ntmRomeynt"eeLmen:fesxeTp=einrts}oa=wgdc/lPtd_s/nifafiFd=(lonrt}irli"i-tohlnar[nbiCmfeatne/mo}rUmCaxogn"oReomtc-fpfmL(me/reiarT_p,c/lgto"o_ao*e;hm@Pftr*m"eai(e"e;"stl{-,nulhewtri(ne""lniabp:"tmm-u;/pevb"eo)ilosr;tifltacfi.l/"nmsm,te"ort,dcae"."l;unsre/lx*)t*;/"t]y,pescript"),

After:

iic]emmo;xppn{}{}poos,,orrt..ir}rttnngu,teeenl"nnsxxoe@deelttrsnexxiCTe:efttnoysxaCTtnp:{tuoyCfe/lnpoiS[ntfengc"eiSf,rsxegciirtsrgpc/lfit/nirp=Clonotoi-tmC[nbiCof/mo"niognefgc-fsi,reilg/lgi*e;nf*mtr"e-o,ncmto""n"p:feuisb"gllo-iifnncfet/"x-m,tco/odcneoflriseg/--*wn*ee"bx,-tv/"itstycaprleissp"ct;rsi/p*t*"";],

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 using await
  • 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 ItemResult
npm run typecheckPass
npm run lintPass (only 1 existing warning)
npm run buildTurbopack build successful
npm run devlocalStorage error resolved

Summary

  • Root cause: Web Storage API was enabled by default in Node.js 25, causing the localStorage object to exist but methods to not function without --localstorage-file setting. 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.ts to proxy.ts, removal of next 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.