App Routerとは?(Next.js 13+ ルーティングシステムガイド)

December 28, 2025
App Routerは、Next.js 13+で導入された新しいルーティングとレンダリングアーキテクチャです。appディレクトリ、ネストされたレイアウト、React Server Componentsを基盤とし、Streaming、並列ルート、より明確なデータ取得パターンをサポートします。
Next.js Core

App Routerとは?

App Routerは、Next.js 13+で導入された新しいルーティングシステムおよびアプリケーションアーキテクチャです。app/ディレクトリを使用した規約ベースのルーティング(ファイルがルートを定義)を採用し、**React Server Components(RSC)**をデフォルト機能として活用することで、レイアウト、ページ、ローディング状態、エラー処理、データ取得の方法を根本から再設計しています。

Pages Routerを「ページ中心(pages/)」と考えるなら、App Routerは「ルートセグメントとレイアウトツリー中心(app/)」と言えます。各ルートセグメントは独自のレイアウト、ローディング状態、エラーバウンダリを持つことができ、それらが組み合わさって完全なUIツリーを形成します。

なぜ重要なのか?

  1. 強化されたレイアウトとナビゲーション体験:ネストされたレイアウト、永続化されたレイアウト、部分的な更新をサポートし、冗長なレンダリングとリクエストを削減します。
  2. デフォルトでServer-first(JSの削減):ページはデフォルトでServer Componentとなり、より多くのレンダリングとデータ取得をサーバー側で行えるため、クライアント側のバンドルサイズが削減されます。
  3. 改善されたローディングとエラーバウンダリ:各ルートセグメントで独立してloading.tsx / error.tsxを定義でき、ユーザー体験と保守性が向上します。
  4. より自然なフルスタック機能:Route Handlers(app/api/**/route.ts)とServer Actionsにより、モノレポでのフルスタック開発がよりシームレスになります。
  5. 将来の機能の基盤:並列ルート、インターセプティングルート、Streaming/Suspenseなどの機能が、App Routerでより完全にサポートされます。

コアコンセプトと動作原理

ルートセグメントと規約ファイル

App Routerでは、URLは通常app/以下のディレクトリ構造によって決定されます。

  • page.tsx:このセグメントのページエントリーポイント
  • layout.tsx:このセグメントとその子セグメントで共有されるレイアウト(ネスト可能)
  • loading.tsx:このセグメントのローディングUI(Suspenseベース)
  • error.tsx:このセグメントのエラーバウンダリ(Client Componentである必要があります)
  • not-found.tsx:このセグメントの404ページ

最小限の例:

// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body>{children}</body>
    </html>
  );
}
// app/page.tsx
export default function HomePage() {
  return <main>ホームページ</main>;
}

Server Components vs Client Components

  • デフォルトはServer Component"use client"を記述していないコンポーネントはサーバー上でレンダリングされ、サーバーリソース(データベース、プライベートAPI、環境変数など)に直接アクセスできます。
  • インタラクティブな機能にはClient Component:ボタンのクリック、即座のフォームバリデーション、useState/useEffectの使用などに使います。
// app/dashboard/page.tsx (Server Component、デフォルト)
import { getServerSession } from "@/lib/auth"; // 例:サーバー側でのセッションアクセス

export default async function DashboardPage() {
  const session = await getServerSession();
  return <div>ようこそ、{session.user.name}さん</div>;
}
// app/dashboard/ClientButton.tsx (Client Component)
"use client";

import { useState } from "react";

export function ClientButton() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount((c) => c + 1)}>クリック数:{count}</button>;
}

ネストされたレイアウトと共有UI

App Routerのレイアウトはツリー構造で構成されます。特定のセグメントに独立したレイアウトを提供しながら、親のレイアウトを継承できます。

// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="dashboard-shell">
      <aside>サイドバー</aside>
      <section>{children}</section>
    </div>
  );
}

データ取得とキャッシング(直感的な説明)

App Routerでは、一般的に以下が推奨されます:

  • Server Componentで直接データを取得する(よりシンプルなアプローチ)
  • 外部へのfetchリクエストはデフォルトでキャッシュと重複排除が行われます(必要に応じてcache / revalidateを設定可能)
  • 「送信後にUIを更新」する場合、Server Actionsを使用するか、revalidatePath/revalidateTagと組み合わせるのが一般的です

(より詳しくは、nextjs-server-actionsnextjs-server-componentsの関連エントリを参照してください。)

実際の使用例

シナリオ1:ローディング状態とエラーバウンダリを持つ詳細ページの構築(動的ルート)

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

async function getPost(slug: string) {
  // 例:API/データベースから取得
  const res = await fetch(`https://example.com/api/posts/${slug}`, { cache: "no-store" });
  if (res.status === 404) return null;
  if (!res.ok) throw new Error("読み込みに失敗しました");
  return res.json();
}

export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug);

  if (!post) notFound();

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  );
}
// app/posts/[slug]/loading.tsx
export default function Loading() {
  return <div>記事を読み込み中...</div>;
}
// app/posts/[slug]/error.tsx
"use client";

export default function Error({ reset }: { reset: () => void }) {
  return (
    <div>
      <p>記事の読み込み中にエラーが発生しました。</p>
      <button onClick={() => reset()}>再試行</button>
    </div>
  );
}

解説page.tsxはサーバー上で実行されデータを取得します。リクエストが保留中の間はloading.tsxが表示され、エラーはerror.tsxでキャッチされます。リソースが見つからない場合はnotFound()を使用してセグメントの404ロジックに入ります。

シナリオ2:Layoutを使用して「アカウントセクション」に統一されたシェルと認証チェックを提供

// app/account/layout.tsx
import { redirect } from "next/navigation";
import { getServerSession } from "@/lib/auth"; // 例

export default async function AccountLayout({ children }: { children: React.ReactNode }) {
  const session = await getServerSession();
  if (!session) redirect("/login");

  return (
    <div className="account">
      <header>アカウントセンター</header>
      <main>{children}</main>
    </div>
  );
}

解説:「認証 + 統一されたシェル」をlayout.tsxに配置することで、app/account/**以下のすべてのページが自動的に保護され、各ページで繰り返し認証チェックを書く必要がなくなります。

ベストプラクティス

💡 ヒント1:デフォルトでServer Componentを使用し、インタラクティブな機能が必要な場合のみClient Componentを使用する。これにより、クライアント側のJSが大幅に削減され、データアクセスがより直感的になります。

  1. 共有UIをレイアウト層に配置する:ナビゲーション、サイドバー、認証チェック、グローバルプロバイダーなどは、主にlayout.tsxに記述します。
  2. 重要なルートセグメントにバウンダリファイルを追加する:コアページにloading.tsx / error.tsx / not-found.tsxを追加すると、ユーザー体験が大幅に向上します。
  3. 動的ルートとデータ取得を予測可能にする:どのページがリアルタイムデータ(no-store)を必要とし、どのページが増分更新(revalidate)を使用できるかを明確にします。
  4. ルート構造でビジネスロジックを表現する:例えば、app/(marketing)app/(app)のようなルートグループ(括弧付きディレクトリ)を使用すると、URLをクリーンに保ちながら構造をより明確にできます。

⚠️ よくある落とし穴:Server Componentでブラウザ APIやReact hooks(useState/useEffect)を直接使用すること。インタラクティブな機能が必要なコンポーネントには"use client"を追加し、明確な境界を維持してください。

よくある質問

App RouterとPages Routerのどちらかを選ばなければなりませんか?

回答:いいえ、共存できます。段階的に移行でき、新機能をapp/に配置しながら、古いページは移行が完了するまでpages/に残しておくことができます。

なぜerror.tsxはClient Componentでなければならないのですか?

回答:エラーバウンダリはクライアント側のエラー回復メカニズム(reset()など)に依存しているため、Next.jsではerror.tsx"use client"を付ける必要があります。

layout.tsxはナビゲーションのたびに再レンダリングされますか?

回答:通常はされません。レイアウトは永続化できます(特に同じレイアウトツリー内でのナビゲーション時)。これは、App Routerが体験とパフォーマンスを向上させる重要な方法の1つです。

Route Handlers(app/api/**/route.ts)はいつ必要ですか?

回答:HTTP API(サードパーティのコールバック、Webhook、モバイルアプリ、外部システム向け)を提供する必要がある場合、Route Handlersが理想的です。フォーム送信のみの場合は、Server Actionsも検討できます。

他のコンセプトとの比較

機能App RouterPages Router
ルーティングディレクトリapp/pages/
デフォルトのレンダリングモードServer Components優先クライアント/サーバーハイブリッド(ページ中心)
レイアウト機能ネストされたレイアウト、より強力な永続化手動でのラッピングと構成が必要
Loading/Errorバウンダリルートセグメントレベルでネイティブサポートカスタム実装が必要
新機能のサポート並列/インターセプティングルート、Server Actionsなど限定的、または追加のソリューションが必要

さらに学ぶ

関連用語

  • nextjs-server-components
  • nextjs-server-actions
  • nextjs-middleware
  • nextjs-dynamic-routes
  • nextjs-parallel-routes
  • nextjs-metadata-api