1. ホーム
  2. Next.js

【Next.js】global-error.tsx の使い方|ルートレイアウトのエラーも受け止める方法を解説

Share

Next.js の App Router では、error.tsx を置くことでそのセグメント配下のエラーを捕まえてエラー画面を出せます。ところが、ルートレイアウト(app/layout.tsx)そのものでエラーが起きた場合、app/error.tsx はレイアウトの内側に描画されるため出番がありません。この「レイアウト自体が壊れたとき」の最後の受け皿が global-error.tsx です。この記事では、global-error.tsx の書き方、error.tsx との違い、htmlbody を自分で書かなければならない理由、そして開発中は見えないという挙動までを解説します。

global-error.tsx とは(アプリ全体の最後の受け皿)

app/global-error.tsx は、App Router のルート階層に置く特別なファイルです。ルートレイアウトやルートテンプレートで発生したエラーを含む、アプリケーション全体のエラーを捕捉します。error.tsx がそのセグメントのレイアウトの内側に描画されるのに対し、global-error.tsx はルートレイアウトを置き換えて描画されるのが最大の違いです。

ルートレイアウトを置き換えるということは、そこで定義していた <html><body> も一緒に失われるということです。そのため global-error.tsx では、これらのタグを自分で書く必要があります。共通のヘッダーやフッター、グローバル CSS の読み込みもルートレイアウトごと失われるので、シンプルな画面にしておくのが安全です。

基本の書き方

error.tsx と同じく、global-error.tsx は必ずクライアントコンポーネント('use client')にします。エラーの内容を受け取る error と、再描画を試みる reset の2つの props を受け取ります。

app/global-error.tsx
'use client';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    // ルートレイアウトを置き換えるので html・body を自分で書く
    <html lang="ja">
      <body>
        <h2>問題が発生しました</h2>
        <p>お手数ですが、しばらくしてから再度お試しください。</p>
        <button onClick={() => reset()}>もう一度試す</button>
      </body>
    </html>
  );
}

htmlbody を書き忘れると、エラー画面が正しく描画されません。「エラーが起きたのに真っ白な画面になる」という症状の多くはこれが原因です。

props の中身(error と reset)

受け取る2つの props の役割は次のとおりです。digest は本番環境で重要になるプロパティで、エラーメッセージの代わりにサーバーログと突き合わせるための識別子です。

props説明
error発生した Error オブジェクト。本番ではメッセージが「Application error: a server-side exception has occurred」のような汎用文言に置き換えられる
error.digestサーバーで生成されたエラーのハッシュ。サーバーログの該当エラーと突き合わせるために使う
resetエラー境界の再描画を試みる関数。成功すれば元の画面に戻る

本番でエラーの詳細が伏せられるのは、機密情報の漏洩を防ぐための仕様です。原因を追いたいときは digest をユーザーに表示しておき、問い合わせを受けたらその値でサーバーログを検索する、という運用にすると調査がしやすくなります。

app/global-error.tsx
'use client';

import { useEffect } from 'react';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // 監視サービスへの通知など。ここは必ずクライアント側で動く
    console.error(error);
  }, [error]);

  return (
    <html lang="ja">
      <body>
        <main>
          <h2>問題が発生しました</h2>
          {error.digest && (
            <p>エラーID: <code>{error.digest}</code></p>
          )}
          <button onClick={() => reset()}>もう一度試す</button>
          <a href="/">トップページへ戻る</a>
        </main>
      </body>
    </html>
  );
}

トップページへのリンクに next/link ではなく <a> を使っている点にも意味があります。ルートレイアウトが壊れている状況ではクライアント側のルーティングが正常に動かないことがあるため、ページ全体を読み込み直す通常のリンクの方が確実です。

error.tsx との違いと使い分け

2つのファイルは似ていますが、捕捉する範囲と描画のされ方が異なります。日常的なエラー表示は error.tsx が担当し、global-error.tsx はそこで拾えなかった場合の保険という位置づけです。

観点error.tsxglobal-error.tsx
置く場所任意のセグメントapp/ の直下のみ
捕捉する範囲同じセグメントの page と配下の子ルートレイアウトを含むアプリ全体
描画される位置そのセグメントのレイアウトの内側ルートレイアウトを置き換える
html / body不要(レイアウトが持っている)自分で書く必要がある
共通ヘッダー等そのまま残る失われる

両方を置いた場合、まず近い方の error.tsx が処理を試み、それでも捕まえられなかったとき(=ルートレイアウトやルートテンプレート自体のエラー)に global-error.tsx が使われます。多くのアプリでは app/error.tsxapp/global-error.tsx を両方用意しておくのが定石です。

捕捉できないエラーもある

global-error.tsx は万能ではありません。React のエラー境界は、あくまでレンダリング中に発生した例外を捕まえる仕組みです。次のようなものは対象外なので、別の方法で対処します。

まず、イベントハンドラーの中で投げられたエラーは捕捉されません。onClick の処理が失敗する可能性があるなら、その中で try...catch して状態に持たせ、画面に反映する必要があります。同じく setTimeout や Promise のコールバックなど、レンダリング後に非同期で走る処理のエラーもエラー境界の外です。

また、notFound()redirect() が内部的に投げる特殊なエラーは Next.js が専用に処理するため、エラー画面にはなりません。not-found.tsx が表示されたり、リダイレクトが実行されたりする正常な動作です。ただし、try...catch の中でこれらを呼ぶと catch 節に吸い込まれて動かなくなるので、try ブロックの外で呼ぶようにしてください。

開発中に global-error.tsx が表示されないとき

開発モードではエラーオーバーレイが優先される

next dev で動かしていると、エラーが起きても自作のエラー画面ではなく Next.js の開発用オーバーレイ(赤いエラー表示)が出ます。これは仕様で、開発中はスタックトレースを見せる方が有用だからです。global-error.tsx の見た目を確認したいときは、next buildnext start で本番モードを起動して試してください。

‘use client’ を書き忘れている

エラー境界は内部的にクラスコンポーネントの componentDidCatch を使う仕組みで、クライアント側でしか動きません。'use client' がないとビルド時にエラーになります。ファイルの先頭行に必ず書いてください。

html・body を書いていない

前述のとおり、global-error.tsx はルートレイアウトを置き換えます。<div> だけを返していると html 要素が存在しない状態になり、画面が真っ白になります。error.tsx からコピーして作るときにやりがちなミスです。

グローバル CSS が効かない

ルートレイアウトで import './globals.css' していた場合、そのレイアウトごと置き換わるため、エラー画面ではスタイルが当たらないことがあります。エラー画面用の最低限のスタイルは、インラインスタイルで直接書くか、global-error.tsx の中で CSS を読み込む形にしておくと確実です。

まとめ

app/global-error.tsx は、ルートレイアウトを含むアプリケーション全体のエラーを受け止める最後の砦です。error.tsx がレイアウトの内側に描画されるのに対し、global-error.tsx はルートレイアウトを置き換えるため、<html><body> を自分で書く必要があります。ファイル先頭の 'use client' は必須で、errorreset の2つの props を受け取ります。本番ではエラーメッセージが伏せられるので、error.digest を画面に出しておくとサーバーログとの突き合わせに役立ちます。イベントハンドラー内や非同期処理のエラーは捕捉されないこと、notFound()redirect() は対象外であることも押さえておきましょう。開発モードではエラーオーバーレイが優先されるため、表示を確認するときは next buildnext start で本番モードを起動してください。

参考ページ