できない.dev

Next.js で「ReferenceError: window is not defined」が解消できない

サーバー側(SSR・ビルド時のプリレンダ)で window や document を参照しているのが原因。
ブラウザ専用コードは useEffect 内へ移すか、next/dynamic の ssr: false で読み込む。

公開: 更新:

実行例あり(2026-09-16 に実環境で検証)

要約

ReferenceError: window is not defined は、サーバー側で実行されるコードがブラウザ専用のオブジェクトに触れたときに出ます。

Next.js はページをサーバーやビルド時にいったん描画(プリレンダ)します。
その時点に window は存在しません。

直し方は「ブラウザでだけ動かす」ことの徹底です。

実行例

App Router の最小プロジェクトを作り、'use client' を付けたコンポーネントのレンダー本体で window.innerWidth を読んで next build にかけてみる。'use client' があってもビルド時のプリレンダはサーバーで走るため ReferenceError: window is not defined で終了コード 1 になり、useEffect へ移すと同じビルドが通る。

$ npx next build > build.log 2>&1; echo "終了コード: $?"; grep -E 'window is not defined|prerendering page|Export encountered' build.log
終了コード: 1
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: window is not defined
Export encountered an error on /page: /, exiting the build.
$ npx next build > build.log 2>&1; echo "終了コード: $?"; grep -E 'Compiled successfully|✓ Generating static pages|window is not defined' build.log
終了コード: 0
 ✓ Compiled successfully in 5.3s
 ✓ Generating static pages (4/4)
$ npx next build > build.log 2>&1; echo "終了コード: $?"; grep -E 'window is not defined|prerendering page|Export encountered' build.log
終了コード: 1
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: window is not defined
Export encountered an error on /page: /, exiting the build.
$ npx next build > build.log 2>&1; echo "終了コード: $?"; grep -E 'Compiled successfully|✓ Generating static pages|window is not defined' build.log
終了コード: 0
 ✓ Compiled successfully in 2.7s
 ✓ Generating static pages (4/4)

— 2026-09-16 時点の出力

検証環境

検証日
実行環境
node:20Debian GNU/Linux 12 (bookworm)
バージョン
  • Node.js 20.20.2
  • npm 10.8.2
  • Python 3.11.2
  • Git 2.39.5

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. Server Component やモジュールのトップレベルで window / document / localStorage を読んでいる。
  2. ブラウザ前提のライブラリを通常 import し、ビルド時に評価されている。
  3. useEffect の外、つまりレンダー本体でブラウザ API を呼んでいる。

解決策

1. useEffect の中へ移す

useEffect はクライアントでのみ実行されるため、ブラウザ API はここに置きます。

'use client';
import { useEffect, useState } from 'react';
 
export function Width() {
  const [w, setW] = useState(0);
  useEffect(() => {
    setW(window.innerWidth); // クライアントでのみ実行
  }, []);
  return <span>{w}</span>;
}

2. ssr:false で動的 import する

コンポーネントごとプリレンダから外したいときは、Client Component から next/dynamic を使います。

'use client';
import dynamic from 'next/dynamic';
 
const Chart = dynamic(() => import('./Chart'), { ssr: false });

3. typeof でガードする

単発の参照なら、アクセス前に存在チェックを入れます。

if (typeof window !== 'undefined') {
  // ここはブラウザでのみ動く
}

この記事は役立ちましたか?