【解決】 Gatsby: Window is not defined の解決方法と原因 | Gatsby/React トラブルシューティング

Gatsbyで開発中に「Window is not defined」というエラーに遭遇し、お困りではないでしょうか?ご安心ください、このエラーはGatsbyやNext.jsのようなサーバーサイドレンダリング(SSR)を行うフレームワークでは非常によく発生するものであり、解決策は明確です。この記事では、このエラーの原因から、今すぐ試せる最も簡単な解決策、そして恒久的な再発防止策まで、Windowsユーザー向けに具体的に解説します。

1. Gatsby: Window is not defined とは?(概要と緊急度)

Window is not defined」エラーは、Gatsbyがウェブページをビルドまたはレンダリングする際に、サーバーサイド(Node.js環境)でブラウザ固有のグローバルオブジェクトであるwindowにアクセスしようとしたときに発生します。

  • Gatsbyの動作原理: Gatsbyは、サイトの表示速度を最適化するために、事前にHTMLファイルをサーバーサイドで生成します(SSR/SSG: Server-Side Rendering/Static Site Generation)。この際、コードはNode.js環境で実行されます。
  • エラーの原因: windowオブジェクト(やdocumentオブジェクトなど)はブラウザ環境でのみ存在します。そのため、Node.js環境でwindowにアクセスしようとすると、「そんなものはない!」とエラーが発生するわけです。
  • 緊急度: このエラーが発生すると、ページのビルドや開発サーバーの起動が失敗するため、開発が完全に停止します。したがって、緊急度は非常に高い問題です。

2. 【最速】今すぐ試すべき解決策

このエラーに遭遇した場合、最も早く、そして最も一般的な解決策は、windowオブジェクトが存在するかどうかをチェックする条件分岐を導入することです。

解決策1:typeof window !== 'undefined' を使用する最も簡単な方法

windowオブジェクトにアクセスするコードの前に、typeof window !== 'undefined' という条件を追加することで、そのコードがブラウザ環境でのみ実行されるように制御できます。特に、ReactのuseEffectフック内でブラウザAPIを扱う際に有効です。

修正例:

例えば、以下のようなコードでエラーが発生しているとします。

// エラーを発生させる可能性のあるコード例
import React, { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    // この行で 'window is not defined' エラーが発生する可能性
    console.log('Window width:', window.innerWidth);
  }, []);

  return <div>Hello Gatsby</div>;
};

export default MyComponent;

これを、以下のように修正してください。

// 修正後のコード例
import React, { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    // window オブジェクトが定義されているかを確認
    if (typeof window !== 'undefined') {
      console.log('Window width:', window.innerWidth);
      // ここに window やその他のブラウザAPIに依存する処理を記述
      // 例: window.addEventListener('resize', ...)
    }
  }, []); // 依存配列は適切に設定してください

  return <div>Hello Gatsby</div>;
};

export default MyComponent;

変更適用後の手順(Windowsコマンド):

コードを修正したら、Gatsbyの開発サーバーを再起動して変更を適用します。

  1. 現在実行中のGatsby開発サーバーを停止します。通常は、コマンドプロンプトやPowerShellでCtrl + Cを押し、Yキーで終了します。
  2. 以下のコマンドを実行して、開発サーバーを再起動します。
gatsby develop

または、プロジェクトのpackage.jsonに定義されているスクリプトを使用している場合は:

npm run develop

これにより、エラーが解消され、無事に開発サーバーが起動するはずです。

3. Gatsby: Window is not defined が発生する主要な原因(複数)

このエラーは、主に以下のシナリオで発生します。

  • SSR時にブラウザAPIへの直接アクセス: コンポーネントのルートレベルやuseEffect以外の場所で、windowdocumentlocalStorageなどのブラウザ固有のグローバルオブジェクトに直接アクセスしている場合。GatsbyがNode.js環境でコードを実行する際に、これらのオブジェクトが存在しないためエラーになります。
  • 外部ライブラリがSSRに対応していない: 使用しているReactコンポーネントやJavaScriptライブラリが、その内部でブラウザAPIに直接アクセスしており、かつSSR環境での実行を考慮していない場合。特に、UIライブラリや特定のユーティリティライブラリで発生しやすいです。
  • 意図しないグローバルスコープでの実行: Gatsbyのビルドプロセス中に、ビルド時(SSR時)に実行されてはいけないコードが、グローバルスコープやコンポーネントのレンダリングフェーズで実行されてしまう場合。

4. Gatsby/Reactで恒久的に再発を防ぐには

一時的な解決だけでなく、今後の開発で「Window is not defined」エラーを再発させないためのプラクティスを身につけましょう。

4.1. typeof window !== 'undefined' を適切に利用する

基本的な解決策ですが、これを徹底することが最も重要です。windowオブジェクトに依存するすべてのコードは、この条件チェックで保護すべきです。特にuseEffectフック内で処理を囲むのがベストプラクティスです。

4.2. 動的インポート(Dynamic Imports)を活用する

ブラウザ環境でしか動作しないコンポーネントやライブラリ全体がある場合、GatsbyのSSRプロセス中にそれらがロードされないように、動的インポートを利用します。これにより、クライアントサイドで初めてコンポーネントがマウントされる際にロードされます。

// 例: クライアントサイドでのみ表示されるコンポーネント
import React, { useEffect, useState } from 'react';
import loadable from '@loadable/component'; // loadable-components ライブラリを使用

const ClientOnlyComponent = loadable(() => import('./ClientOnlyModule'));

const MyPage = () => {
  const [isClient, setIsClient] = useState(false);

  useEffect(() => {
    setIsClient(true);
  }, []);

  return (
    <div>
      <h1>My Awesome Page</h1>
      {isClient && <ClientOnlyComponent />}
    </div>
  );
};

export default MyPage;

@loadable/componentのようなライブラリは、Gatsbyと相性が良く、簡単に動的インポートを実現できます。

4.3. GatsbyのBrowser APIを活用する

Gatsbyには、クライアントサイドでのみ実行される特定のロジックを記述するためのBrowser APIが用意されています。具体的には、gatsby-browser.jsファイルで提供されるAPIを利用します。

例: onClientEntryフックを使用する

// gatsby-browser.js
export const onClientEntry = () => {
  // クライアントサイドでのみ実行したいグローバルな初期化処理など
  if (typeof window !== 'undefined') {
    // 例: 特定の外部スクリプトのロード
    console.log("Client-side entry point reached!");
  }
};

4.4. 外部ライブラリの選定と対応

  • SSR対応の確認: 外部ライブラリを導入する際は、そのライブラリがGatsbyのようなSSRフレームワークに対応しているか(公式ドキュメントに記載があるかなど)を確認しましょう。
  • ラッパーコンポーネントの作成: SSRに対応していないライブラリでも、前述のtypeof window !== 'undefined'チェックや動的インポートを使って、独自のラッパーコンポーネントを作成し、その内部で安全に利用できる場合があります。

これらの対策を講じることで、Gatsbyプロジェクトにおける「Window is not defined」エラーの発生を大幅に減らし、スムーズな開発を進めることができるでしょう。焦らず、原因を特定し、適切な解決策を適用してください。