---
title: "Next.js Pages Router と App Router、ごちゃ混ぜになりがちな違いを整理する"
date: 2025-05-18
categories: column
author: 平林
canonical: https://www.liberogic.jp/topics/20250519-nextjs/
---

# Next.js Pages Router と App Router、ごちゃ混ぜになりがちな違いを整理する

![](https://images.microcms-assets.io/assets/4b13731f29254025b91c8d846198ffc9/16cf5328e035426d91554959cfe87aec/cover.jpg)

Next.jsのPages Router と App Router の両方のプロジェクトに関わる中で「どっちの書き方だっけ？」と混同する方もいらっしゃるかと思います。「ごちゃ混ぜになりがちな」ポイントに焦点を当て、それぞれの主要な違いを簡単にではありますが備忘録として記載します！

Web開発フレームワークとして多くの支持を集める Next.js は、バージョン13で新しい App Router がデフォルトのルーティングシステムとして導入されました。

Pages Router と App Router の両方のプロジェクトに関わる中で「どっちの書き方だっけ？」と混同する方もいらっしゃるかと思います。まさに私です！！

特に、ファイル構造に基づいたルーティングの定義方法から始まり、URLの動的な部分の扱い方、さらにはレンダリングの仕組み、そしてAPIからのデータ取得といった、アプリケーション開発の根幹に関わる部分で、Pages Router と App Router には無視できない違いが存在します。

「ごちゃ混ぜになりがちな」ポイントに焦点を当て、Pages Router と App Router の主要な違いを簡単にではありますが備忘録として記載します！

## ファイルのルーディングの違い

#### Pages Router：ファイル名がそのままパスになるシンプルさ

pagesディレクトリ配下に配置した `.js`, `.jsx`, `.ts`, `.tsx` ファイルのファイル名が、そのままURLのパスになるというシンプルさです。

例：`src/page/about.tsx`　⇒　ルーティングは、`/about`　となります。

#### App Router：フォルダと特定のファイル名で構造化

App Router は`src/app`配下にフォルダやファイルを置く事で、ルーティングできます。

ただし、ルーティングに利用するファイル名にpageという特定のファイル名をつけたものが、そのパスに対応するページのコンテンツとして扱われます。

例：`src/app/about/page.tsx`　⇒　ルーティングは、`/about`　となります。

### App Router を特徴づける「特別なファイル」たち

またApp Routerではpage.tsx以外にも、意味を持つファイルがあります。

```
`app/
├── page.tsx           --> ページの中身。これが一番よく使うファイル
├── route.tsx          --> APIの定義（page.jsと共存不可）
├── layout.tsx         --> 共通の見た目
├── loading.tsx        --> 読み込み中の画面
├── error.tsx          --> エラー時の画面
├── global-error.tsx   --> グローバルエラー画面
├── templete.tsx       --> 共通の見た目（リセットされるレイアウト）
├── default.tsx        --> デフォルトの画面
└── not-found.tsx      --> notFound関数がスローされたときの画面
`
```

個人的に驚いたのがnot-found.tsx。このページを用意しておくだけで存在しないURLへのアクセス時に自動的に表示されるようになります。こんなに簡単に実装できてしまうのか、、と感心しました。

### レンダリングの違い

Pages Router におけるコンポーネントは、基本的にはクライアントサイドでインタラクティブになることを前提としており、サーバーサイドでのレンダリングは主に初期表示の高速化やSEOのために行われます。`useState` や `useEffect` といった React のフックは、ページコンポーネント内やその子コンポーネント内で自由に使用できます。

一方でApp Routerの`app/`内のコンポーネントは、**明示的に指定しない限り、デフォルトでサーバーコンポーネントとして扱われます**。そのため`useState` や `useEffect` といった**React のフックは使用できません**。クライアント側のアクション、onClickなどの**イベントハンドラも使用できません。**

そこでApp RouterでCSRにするには`use client`を記述します。

### use client

Server Component と Client Component の**境界の宣言**です。`use client`を宣言したらそのコンポーネントだけではなく、インポートしてるすべても Client Component としてみなされる

## 動的ルーディング [○○]

ブログやニュースの記事詳細ページのように、URLの一部がユーザーやコンテンツによって変化する「動的なURL」に対応するための仕組みが、Next.js の**ダイナミックルーティング**です。

例えば記事A、記事Bに遷移するリンクがあるとします。

![例えば記事A、記事Bに遷移するリンクが並ぶページのキャプチャ](https://images.microcms-assets.io/assets/4b13731f29254025b91c8d846198ffc9/43840912cd0a4320a6c865bd17b5c1ac/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202025-05-16%2017.41.36.png)

```
`import Link from "next/link";
const linkStyle=`ext-lg font-bold border-b border-primary px-1`

export default function page() {
  return (
    <div>
      <div className="flex gap-6">
        <Link href="/post/A" className={linkStyle}>記事Aへのリンク</Link>
        <Link href="/post/B" className={linkStyle}>記事Bへのリンク</Link>
      </div>
    </div>
  );
}`
```

リンクそれぞれの遷移先

![post/A ページのキャプチャ](https://images.microcms-assets.io/assets/4b13731f29254025b91c8d846198ffc9/78857b655c5148cba2f603aedd7d9f8c/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202025-05-16%2017.51.02.png)

![post/B ページのキャプチャ](https://images.microcms-assets.io/assets/4b13731f29254025b91c8d846198ffc9/358c22d13b224583ab26be920a4eeacf/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202025-05-16%2017.50.47.png)

動的ルーディングにする場合、`[slug]`というファイルを作成すれば、`post/A`でも`post/B`でも同じテンプレートを使用することができます。   
※[ ]のネーミングは任意なので`slug`でなくてもよい

#### App Routerの場合

/app/post/[slug]/page.tsx

```
`export default aysinc function PostPage({ params }: { params: Promise<{ slug: string }>) {
  const {slug}= await params;
  return (
    <div>
      <h1>記事: {slug}</h1>
    </div>
  );
}
`
```

`params`を使います。`params.slug` に URLの`[slug]`部分が入ります。

※ `params` でのパラメーター取得がこれまで非同期である必要はありませんでしたが、Next.js 15からは非同期で行うように変更されました。そのため、`props` を Promise 型で定義する必要があるようです。

#### Pages Rouerの場合

/pages/post/[slug].tsx

```
`
import { useRouter } from 'next/router';

export default function PostPage() {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <div>
      <h1>記事: {slug}</h1>
    </div>
  );
}
`
```

`useRouter`を使います。`router.query.slug` に、URLの `[slug]` の部分の値が入ります。

## APIデータ取得方法

まさにこのLiberogicコラムでも大活躍しているmicroCMSはNext.jsのようなフレームワークとの相性がよいです。

#### App Routerの場合

シンプルに`fetch`や`aysnc` `await`を使用するでよいです。

ただし、`use client`を宣言したファイルはCSRになるので`aysnc` `await`は使えなくなります！

```
`
async function getPost() {
  const res = await fetch(`https://.../api/data/`);

  // エラーハンドリング例
  if (!res.ok) {
     throw new Error(`Failed to fetch post: ${res.status}`);
  }

  return res.json();
}
`
```

呼び出し先

```
`export default function Example() {
   const data = await getPost()
   // ... data を使って表示 ...
}
`
```

#### Pages Routerの場合

`getStaticProps` を使用し、取得したデータを `props` としてページコンポーネントに渡します。

```
`
export async function getServerSideProps(context) {
  // context.req, context.res などにアクセス可能
  const res = await fetch(`https://.../api/data/`, {
     headers: { Cookie: context.req.headers.cookie } // 例: Cookieを渡す
  });
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}
`
```

呼び出し先

```
`export default function Example({ data }) {
  // ... data を使って表示 ...
}
`
```

## 二つのルーターを理解し、Next.js を使いこなそう！

私の場合、最初にNext.jsを触った案件はApp Routerでした。

その後に別の案件でPages Routerを触り、「新規のページを作成するにはファイル名 index.tsxとpage.tsxどっちだっけ？？」と、なりました。

今回の内容はNext.jsのほんの一部ですが、改めてApp RouterとPages Rouerの違いなどを調べることで自分の中でも整理できました。

個人的にはApp Routerの書き方が分かりやすくて好きなのですが、Pages Routerの案件でもしっかり対応できるようにしたいです。

Next.js をより深く理解し案件や制作物によってそれぞれのルーターを使い分けられるようになるのが理想ですね！
