Node.js 25 + Next.js 15 で発生する localStorage.getItem is not a function エラーの原因と対処法
はじめに
Next.js 15 のプロジェクトで npm run dev を実行したところ、以下のエラーが発生して開発サーバーが正常に動作しなくなりました。
コード上で localStorage を直接呼び出している箇所はなく、原因の特定に時間がかかりました。本記事では、このエラーの根本原因と対処法を解説します。
環境
- Node.js : v25.2.1
- Next.js : 15.3.8
- next-intl : 4.3.5
- OS : macOS (Darwin 25.2.0)
エラーの根本原因
このエラーは Node.js 25 で導入された Web Storage API と Next.js 15 の組み合わせで発生します。
Node.js における localStorage の変遷
Node.js は v22 から Web Storage API(localStorage / sessionStorage)の実装を実験的に進めてきました。
| バージョン | localStorage の挙動 |
|---|---|
| Node.js 21以前 | globalThis.localStorage は undefined |
| Node.js 22〜24 | --localstorage-file フラグ指定時のみ有効(実験的機能) |
| Node.js 25.0.0 | Web Storage API がデフォルトで有効化 |
Node.js 25 では globalThis.localStorage オブジェクトがデフォルトで存在するようになりました。ただし、--localstorage-file フラグで有効なファイルパスを指定しないと、localStorage オブジェクト自体は存在するものの、メソッド(getItem, setItem 等)が機能しないという中途半端な状態になります。
具体的には、Node.js の Issue #60303 で報告されているように、localStorage が「すべてのプロパティアクセスに undefined を返す空のプロキシオブジェクト」になってしまう問題がありました。
なぜ Next.js 15 で問題になるのか
多くのライブラリでは、サーバーサイドでの localStorage アクセスを以下のようなガードで防いでいます。
Node.js 24 以前では globalThis.localStorage が undefined なのでこのガードが正しく機能しましたが、Node.js 25 では localStorage オブジェクトが存在するためガードをすり抜けてしまいます 。そして getItem を呼び出すと、メソッドが undefined なので TypeError: localStorage.getItem is not a function が発生します。
Next.js 15 の内部コードや依存ライブラリの一部がこのパターンに該当しており、サーバーサイドレンダリング時にエラーが発生していました。
対処法の選択肢
この問題には主に2つのアプローチがあります。
方法A: Node.js のバージョンを下げる
Node.js を v24 以下にダウングレードすれば、globalThis.localStorage が undefined に戻るため問題は解消します。ただし、これは一時的な回避策です。
方法B: Next.js 16 にアップグレード(推奨)
Next.js 16 ではこの問題が修正されています(vercel/next-learn#1129)。本プロジェクトではこちらの方法を採用しました。
Next.js 15 → 16 アップグレード手順
1. パッケージの更新
更新されたバージョン:
| パッケージ | 更新前 | 更新後 |
|---|---|---|
| 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. middleware.ts → proxy.ts のリネーム
Next.js 16 の最大の破壊的変更の一つが、Middleware から Proxy へのリネームです。
ファイルの中身は変更不要です。middleware.ts は非推奨として残されていますが、将来のバージョンで削除予定です。
3. lint スクリプトの変更
Next.js 16 では next lint コマンドが削除されました。eslint を直接呼び出すように変更します。
4. ESLint 設定の更新
eslint-config-next@16 はネイティブの flat config をエクスポートするようになりました。@eslint/eslintrc の FlatCompat ラッパーが不要になります。
更新前:
更新後:
5. next.config.ts
webpack のカスタム設定(WASM / ONNX 対応)はそのまま残します。Next.js 16 では Turbopack がデフォルトのバンドラーになりますが、webpack 設定は next build --webpack で引き続き使用可能です。本プロジェクトでは Turbopack で問題なくビルドできたため、変更不要でした。
6. 変更不要だった箇所
params/searchParams: 既にawaitを使った非同期アクセスに移行済み- API routes :
NextRequest.nextUrl.searchParamsを使用しており変更不要 - Client components :
useParams()/useSearchParams()フックは変更不要 - i18n 設定 (
routing.ts,request.ts): 変更不要
検証結果
| チェック項目 | 結果 |
|---|---|
npm run typecheck | ✅ パス |
npm run lint | ✅ パス(既存の warning 1件のみ) |
npm run build | ✅ Turbopack でビルド成功 |
npm run dev | ✅ localStorage エラー解消 |
まとめ
- 根本原因 : Node.js 25 で Web Storage API がデフォルト有効化され、
localStorageオブジェクトが存在するが--localstorage-file未設定時にメソッドが機能しない状態になった。Next.js 15 の内部コードがこの「壊れた localStorage」に対応できていなかった - 対処法 : Next.js 16 へのアップグレードで解消。Node.js のダウングレードでも回避可能
- アップグレード時の注意点 :
middleware.ts→proxy.tsのリネーム、next lintの廃止、ESLint 設定のネイティブ flat config 対応が必要
Node.js のバージョンアップに伴う Web API の追加は、サーバーサイドで動作するフレームワークに思わぬ影響を与えることがあります。特に typeof ガードに頼った実装は、新しい API の追加で壊れる可能性があることを認識しておく必要があります。