Next.jsで「window / document is not defined」が出る理由と、SSRの本質

1. 結論:なぜClient Componentにする必要があるのか?

Next.jsで localStoragesessionStoragewindowdocument を使う際に 'use client'(Client Component)の指定が必要な理由は、これらが「ブラウザ(クライアント)にしか存在しない機能(ブラウザAPI)」だからです。

サーバー側で実行される「Server Component」の環境下では、これらのオブジェクトは存在しない(undefined)ため、アクセスした瞬間にエラーが発生します。


2. 仕組み:サーバー側とブラウザ側の「HTML組み立て」の違い

「サーバー側でHTMLを組み立てる(SSR)」と言っても、サーバー側とブラウザ側では、その裏側の仕組みが全く異なります。

比較項目 サーバー側(Next.jsのSSR) ブラウザ側(従来のJS / Hydration後)
環境 Node.js(サーバー環境) Webブラウザ(Chrome/Safari等)
やっていること JSXをただの「文字列(テキスト)」に変換 本物の「DOMオブジェクト」を生成
主要な手段 React内部の文字列変換エンジン document.createElement() などのDOM API
window / document 存在しない 存在する

サーバー側の処理イメージ

サーバー側は、ブラウザのように画面を管理しているわけではありません。あなたが書いたJSXを、ただの長い文字列として結合しているだけです。

// 概念的なイメージ(実際はより複雑です)
let html = "";
html += "<div>";
html += "<h1>" + props.title + "</h1>";
html += "</div>";
return html; // 結果:"<div><h1>タイトル</h1></div>" という単なるテキストデータ

この段階ではただの「テキストの足し算」をしているだけなので、document オブジェクトは必要ありませんし、存在もしていません。


3. SSRから画面が表示されるまでのステップ(ライフサイクル)

  1. 【サーバー】文字列の組み立て

    Next.jsがコンポーネントを解析し、HTMLの「文字列」を作成してブラウザへ送信します。

  2. 【ブラウザ】受信とHTML解析

    ブラウザが届いたHTML文字列を読み込みます。この瞬間に初めて、ブラウザ内部に windowdocument オブジェクトが生成され、画面に一瞬で文字や絵が表示されます(ただし、まだボタンなどは動きません)。

  3. 【ブラウザ】Hydration(ハイドレーション)

    遅れて届いたJavaScriptがブラウザ上で実行され、すでに表示されているHTML(DOM)に対して、ボタンのクリックイベントなどの「動的な処理」を合体させます。

Next.jsの 'use client'(Client Component)とは、「上記ステップの『2』や『3』のタイミング(ブラウザ側)でも実行されるコードだよ」という宣言です。だからこそ、Client Componentにすることで安全に windowdocument にアクセスできるようになります。

Leave a Comment

CAPTCHA