【Next.js】loading.tsx・error.tsx・not-found.tsxでUXを整える

JavaScript

こんにちは、かつコーチです。

前回は動的ルートとルートグループを使って、URLに応じたページ作りとフォルダ整理の方法を解説しました。

ページの構造ができても、データ取得中の「白い画面」やエラー発生時の「真っ白なエラーページ」をそのまま放置すると、ユーザー体験が悪くなってしまいます。

今回は、App Routerに用意されている特殊ファイル loading.tsxerror.tsxnot-found.tsx を使って、それぞれの状態を見た目よく整える方法を解説します。

loading.tsx・error.tsx・not-found.tsxとは?

3つの特殊ファイルの役割

App Routerでは、page.tsx と同じフォルダに特定のファイル名を置くだけで、特定の状態のUIを自動的に切り替えられます。

  • loading.tsx:データ取得中に表示されるローディングUI
  • error.tsx:レンダリング中に例外が発生したときのエラー画面
  • not-found.tsx:該当データが見つからないときの404画面

いずれも自分でルーティングを書く必要はなく、ファイルを置くだけで自動的に組み込まれます。

なぜそれが必要なのか

これらのファイルがないと、データ取得中は何も表示されずユーザーが不安になったり、エラー時にNext.jsのデフォルトのエラー画面(開発中はスタックトレース)がそのまま表示されたりします。

内部的には、loading.tsxSuspenseの境界、error.tsxError Boundaryの境界として機能しており、Reactの仕組みをNext.jsが自動で配線してくれています。

基本の書き方

手順1:loading.tsxでローディングUIを出す

// app/products/loading.tsx
export default function Loading() {
  return <p className="text-gray-500">商品情報を読み込み中...</p>;
}

同じ階層の page.tsx が非同期でデータを取得している間、Next.jsは自動的にこの Loading を表示してくれます。

手順2:error.tsxでエラー画面を出す

// app/products/[id]/error.tsx
"use client";

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <p>商品情報の取得に失敗しました。</p>
      <p className="text-sm text-gray-400">{error.message}</p>
      <button onClick={() => reset()}>再読み込み</button>
    </div>
  );
}

error.tsx は必ず "use client" を付ける必要があります。

reset 関数を呼ぶと、エラーになった部分だけを再レンダリングし、ページ全体をリロードせずに復旧を試みられます。

手順3:not-found.tsxで404画面を出す

// app/products/[id]/not-found.tsx
export default function NotFound() {
  return <p>指定された商品が見つかりませんでした。</p>;
}

not-found.tsx は、page.tsx の中で notFound() 関数を呼び出したときに表示されます。

// app/products/[id]/page.tsx
import { notFound } from "next/navigation";

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const product = await getProduct(id);

  if (!product) {
    notFound(); // ここでnot-found.tsxが表示される
  }

  return <h1>{product.name}</h1>;
}

つまずきやすい設定・注意点

error.tsx はそのフォルダの layout.tsx 自体で発生したエラーは捕まえられません。

レイアウト自体のエラーまで扱いたい場合は、app/global-error.tsx という特別なファイルを用意する必要があります。

よくあるつまずきポイント・エラー対処

一次情報:’use client’を忘れてビルドエラーになった話

私が初めて error.tsx を作ったとき、他のサーバーコンポーネントと同じ感覚で "use client" を書き忘れてしまいました。

❌ Before:’use client’なしでerror.tsxを書く

// app/products/[id]/error.tsx
export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <p>{error.message}</p>
      <button onClick={() => reset()}>再読み込み</button>
    </div>
  );
}

見た目は普通のコンポーネントなので気づきにくいのですが、ビルド時に「error.tsx must be a Client Component」という趣旨のエラーが出て止まりました。

これは、error.tsx がReactのError Boundaryという仕組みの上に成り立っており、Error Boundaryはクライアント側の機能だからです。

✅ After:ファイル先頭に’use client’を追加

// app/products/[id]/error.tsx
"use client";

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <p>{error.message}</p>
      <button onClick={() => reset()}>再読み込み</button>
    </div>
  );
}

error.tsx はNext.jsのルールとして必ずClient Componentになる、と覚えてしまうのが一番早い解決策です。

まとめ

この記事のポイント

  • loading.tsx はデータ取得中のローディングUIを自動表示する
  • error.tsx はエラー発生時のフォールバックUIで、必ず"use client"が必要
  • not-found.tsxnotFound() 関数を呼んだときに表示される404画面
  • レイアウト自体のエラーには global-error.tsx が必要になる

次に読むべき記事

UI面の整え方が分かったところで、次はApp Routerの核心である「サーバーとクライアントの役割分担」に踏み込んでいきます。

次回は、Server Componentsとは何か、これまでのReactとの一番の違いを解説します。

→ 次の記事:Server Componentsとは?Reactとの一番の違い

タイトルとURLをコピーしました