YASD-TECH
YASD TECH
# Next.js

Pages Router と App Router の違い(パフォーマンス比較つき)

投稿日:2026/7/28

更新日:2026/7/28

ttitleImage

結論

  • App Routerapp/)は React Server Components(RSC)を中心とした現在の標準。Next.js 13 以降で追加され、公式ドキュメントも新機能もこちらが前提。
  • Pages Routerpages/)は Next.js 12 以前からの従来型。今も動くが、新機能は基本的に来ない。
  • 違いはフォルダ構成だけではなく、レンダリング思想(どこで実行されるか)とデータ取得方法が根本的に別物。
  • 新規プロジェクトなら App Router 一択。既存の Pages Router は共存できるので、一括移行せず新しいページから段階的に移していけばよい。

1. ディレクトリ構造とルーティング

Pages Router はファイル名がそのまま URL、App Router はフォルダ名が URL で中の page.tsx が描画する。

Pages Router App Router
静的ページ pages/about.tsx app/about/page.tsx
動的ルート pages/blog/[id].tsx app/blog/[id]/page.tsx
API pages/api/users.ts app/api/users/route.ts

App Router は「フォルダ = ルートセグメント」なので、そのセグメントに関係するファイル(loading.tsxerror.tsx、テストなど)を同じフォルダに同居させられる(コロケーション)。page.tsx が無いフォルダは URL にならないので、app/(admin)/ のようなグルーピングも可能。

2. コンポーネントモデル(RSC の採用)

ここが最大のアーキテクチャ変更。

  • Pages Router: すべてのコンポーネントが「クライアントでも実行される可能性がある」前提。書いたコードは基本的にクライアント JS バンドルに入る。
  • App Router: デフォルトで全コンポーネントが Server Component(サーバー上でのみ実行)。ブラウザに不要な JS を送らない。useState / onClick などインタラクションが必要なものだけ 'use client' を宣言する。
tsx
// app/products/page.tsx — Server Component(デフォルト)
// DB クライアントや重いライブラリはここで完結し、ブラウザには送られない
import { db } from '@/lib/db'
import { AddToCartButton } from './add-to-cart-button'

export default async function Page() {
  const products = await db.product.findMany()

  return (
    <ul>
      {products.map((p) => (
        <li key={p.id}>
          {p.name}
          <AddToCartButton id={p.id} />
        </li>
      ))}
    </ul>
  )
}
tsx
// app/products/add-to-cart-button.tsx
'use client' // ← インタラクションがあるのでここだけクライアント

import { useState } from 'react'

export function AddToCartButton({ id }: { id: string }) {
  const [added, setAdded] = useState(false)
  return <button onClick={() => setAdded(true)}>{added ? '追加済み' : 'カートに追加'}</button>
}

'use client'そのファイル以下の import ツリー全体がクライアント境界に入る点に注意。ページの一番上に付けると App Router の恩恵がほぼ消える。

3. ネストされたレイアウト

  • Pages Router: 全体共通は _app.tsx / _document.tsx。ページごとのネストしたレイアウトは getLayout パターンなど自前の工夫が必要だった。
  • App Router: フォルダごとに layout.tsx を置くだけで自動的にネストされる。ページ遷移しても変化しない親レイアウトは再レンダリングされない(state も DOM も保持される)。
app/
├── layout.tsx              ← 全ページ共通(html/body はここ)
└── dashboard/
    ├── layout.tsx          ← /dashboard/* 共通のサイドバー
    ├── page.tsx
    └── settings/page.tsx   ← 上2つの layout に囲まれる

4. データ取得

Next.js 独自関数が廃止され、標準的な async/await に一元化された。

tsx
// Pages Router: 独自の特殊関数でページ単位で取得し props で流す
export const getServerSideProps = async () => {
  const res = await fetch('https://api.example.com/posts')
  return { props: { posts: await res.json() } }
}
tsx
// App Router: コンポーネント内で直接 await する。キャッシュ設定で SSR/SSG/ISR を切り替え
export default async function Page() {
  const res = await fetch('https://api.example.com/posts', {
    // cache: 'no-store'          → 毎リクエスト取得(SSR 相当)
    next: { revalidate: 60 },     // → 60秒ごとに再生成(ISR 相当)
  })
  const posts = await res.json()
  return <PostList posts={posts} />
}

ページの入口でまとめて取る必要がなくなり、データを使うコンポーネントの中で取る書き方ができるのが大きい。

5. 予約ファイルによる UI 制御

App Router は機能ごとに予約ファイル名が用意されている。詳細は ファイル名ベース仕様メモ(App Router) を参照。

ファイル 役割
page.tsx メイン画面
layout.tsx 共通レイアウト
loading.tsx 取得中のローディング UI(Suspense 連携)
error.tsx エラー時のフォールバック
not-found.tsx 404 UI(not found
route.ts API Route Handler

比較表

項目 Pages Router App Router
位置づけ 従来型(レガシー) 現在の標準(Next.js 13〜)
ベースフォルダ pages/ app/
デフォルト クライアント指向 Server Component(RSC)
レイアウト _app.tsx で一括指定 layout.tsx で柔軟にネスト
データ取得 getServerSideProps / getStaticProps async/await + 拡張 fetch()
ローディング 自前で state 管理 loading.tsx / Suspense
メタデータ(SEO) <Head> コンポーネント metadata / generateMetadata
ストリーミング 実質なし 標準対応

パフォーマンスの差

クライアント側のパフォーマンス(JS 量と体感速度)では App Router が圧倒的に有利。構成次第だが、適切に移行するとブラウザに落ちる JS が 30〜50% 以上削減され、初期表示とページ遷移が目に見えて軽くなるケースが多い。

差を生む要因は 4 つ。

1. JS バンドルサイズの削減

Pages Router では、date-fns・Markdown パーサー・グラフ描画ライブラリなどをコンポーネントで import すると、そのままクライアントバンドルに入っていた。

App Router(Server Component)ならこれらの処理をサーバーで完了させ、結果の HTML だけをブラウザに送れる。ライブラリのコード自体が配信されないので、初期ロード(FCP / LCP)とブラウザ側の JS パース・実行コストが劇的に下がる。

2. ストリーミングによる体感速度の向上

getServerSidePropsサーバー側で全データ取得が終わるまで HTML を 1 文字も返せない(TTFB が悪化する)。

App Router では loading.tsx / Suspense で段階的描画ができる。

  1. ヘッダー・サイドバーなどの共通 UI を数ミリ秒で返して表示する
  2. 時間のかかるデータ(DB 検索など)は取得できた順に後から流し込む

ユーザーからは「すぐ反応した」と感じられるので、数値以上に体感が良くなる。

3. 部分レンダリングによる軽量なページ遷移

  • Pages Router: 遷移のたびにページ全体のコンポーネントとデータ(_next/data)を読み直して描画し直す。
  • App Router: 変更があった差分コンポーネントだけをサーバーから取得して差し替える。親レイアウトは再描画されず DOM と state が維持されるため、SPA 的な軽い遷移になる。

4. fetch の自動重複排除

同一リクエスト内で、複数の深いコンポーネント(ヘッダー・アイコン・サイドバー)がそれぞれ「ログイン中ユーザー情報」を必要とするケース。App Router の拡張 fetch同じリクエストを自動で 1 回にまとめる(Request Deduplication)ので、上からバケツリレーで props を渡す設計にしなくても無駄な通信が増えない。

注意:App Router が遅くなるパターン

  • 'use client' の多用: 付けまくると実質 Pages Router と同じ(レイアウトのオーバーヘッド分わずかに重い)状態になり、恩恵を受けられない。
  • サーバーリソースの消費: Server Component の描画と RSC Payload のシリアライズを行うため、サーバー(Node.js / Edge)の CPU・メモリ負荷は単純な SSR より高くなる傾向がある。
  • ウォーターフォール: await を素直に直列で書くと逐次実行になる。独立したデータは Promise.all で並列化する。

どちらを選ぶか

  • 新規: App Router。公式ドキュメントも今後の新機能も App Router 前提。
  • 既存の Pages Router: 無理に一括移行しなくてよい。pages/app/ は共存可能なので、新しいページや機能から app/ へ寄せていくのが公式にも推奨されているアプローチ。

なお、コンテンツ閲覧中心のサイト(メディア・EC)ほど RSC の恩恵は大きく、フォーム操作が主体の管理画面・SaaS は 'use client' の比率が上がるため差は小さくなる。ただし後者でも、レイアウトのネストとストリーミングのメリットは残る。

学習リソース

Index

  • 結論
  • 1. ディレクトリ構造とルーティング
  • 2. コンポーネントモデル(RSC の採用)
  • 3. ネストされたレイアウト
  • 4. データ取得
  • 5. 予約ファイルによる UI 制御
  • 比較表
  • パフォーマンスの差
  • 1. JS バンドルサイズの削減
  • 2. ストリーミングによる体感速度の向上
  • 3. 部分レンダリングによる軽量なページ遷移
  • 4. fetch の自動重複排除
  • 注意:App Router が遅くなるパターン
  • どちらを選ぶか
  • 学習リソース