👁

Next.jsの基本

この章では、Next.jsの基本について概要を解説し、ハンズオン形式で実際に動作させながら学習します。これにより、App Routerによるファイルベースルーティング・レイアウト共通化・Server/Client Componentの使い分けなど、Next.jsで画面を作る基本が身につきます。

1. 本章の概要

1.1 本章の目的

React単体でWebアプリケーションを本格的に作ろうとすると、ルーティング・レンダリング方式・API連携・ビルド設定を自前で用意する必要があります。Next.jsはこれらを標準機能として取り込んだフレームワークで、SPA(Single Page Application)だけでなくSSR・SSGといった複数のレンダリング方式を、1つのプロジェクトの中でページ単位に使い分けながらアプリケーションを構築できます。業務でReactを採用する場面ではデファクトの選択肢になっています。以降のスタイリング・データ取得・認証の各章の土台になるため、本章ではNext.jsの位置づけとApp Routerによるファイルベースルーティングに加えて、レイアウトの共通化、Server ComponentとClient Componentの使い分け、動的ルーティング・リンク・ナビゲーションも扱います。

1.2 ハンズオンの流れ

本章では、create-next-app で新規プロジェクトを立ち上げ、複数のページとレイアウトを追加します。Server ComponentとClient Componentを切り替えながら、Next.jsの構造とレンダリング挙動を実際に操作しながら確認します。

1.3 事前準備

必要なツール

この章では、以下のツールを使用します。まだインストールしていない場合は、リンク先の手順に沿って準備をお願いします。

ツール名 関連箇所 理由
Visual Studio Code Visual Studio Codeのインストール Next.jsアプリケーションのコードを記述するエディタとして使用する
Node.js Node.jsをインストールしよう Next.jsアプリケーションの実行環境として使用する

2. Next.jsの概要

まずはハンズオンに入る前に、Next.jsがどんなフレームワークで、なぜApp Routerを扱うのかを整理します。Server ComponentとClient Componentの違いも、この後のハンズオンで何度も出てくる中心的なキーワードなので、ここで基本の輪郭を把握しておきます。

2.1 Next.jsとは

Next.jsは、Reactを土台にしたフルスタックのWebアプリケーションフレームワークです。React単体では、ルーティング・ビルド設定・レンダリング方式・データ取得の仕組みなどを自分で用意する必要がありますが、Next.jsはそれらを標準機能として一通り備えており、ページ単位でレンダリング方式を選びながらアプリケーションを構築できます。

Next.jsが扱うレンダリング方式は主に3種類あります。

方式 略称 概要
クライアントサイドレンダリング CSR JavaScriptがブラウザで動いて画面を描画する。従来のReactの標準的な動き方に相当する
サーバサイドレンダリング SSR リクエスト時にサーバでHTMLを作って返す。動的なコンテンツを扱いつつ初期表示を速くできる
静的サイト生成 SSG ビルド時にHTMLを生成しておく。CDN配信と相性がよく、非常に高速に配信できる

Next.jsでは、これらをページ単位で使い分けられるのが大きな特徴です。ブログ記事の詳細ページはSSG、ログイン後のダッシュボードはSSR、と組み合わせられます。

Next.jsには、ページとルーティングを定義する仕組みとして App RouterPages Router の2種類があります。Pages Routerは初期からある仕組みで、pages/ フォルダにファイルを置く方式です。App RouterはNext.js 13で導入された新しい方式で、app/ フォルダに page.tsxlayout.tsx を配置します。App RouterがNext.js公式の推奨方式であり、新規プロジェクトはApp Routerで作成することが推奨されています(Installation(Next.js公式ドキュメント)create-next-appのプロンプト例に「Would you like to use App Router? (recommended) No / Yes」と明記されています)。本章では、このApp Routerを使ってハンズオンを進めます。

2.2 App Routerとは

App Routerは、app/ フォルダのファイルシステム構造そのものがそのままURLになるルーティング方式です。ページを追加したいときは、対応するフォルダとファイルを作るだけでURLが割り当てられます。書き方は以下のとおりです。

app/
├── page.tsx              ← ルート(`/`)に対応するページ
├── パス名/
│   └── page.tsx          ← `/パス名` に対応するページ
└── [動的セグメント]/
    └── page.tsx          ← `/任意の値` に対応する動的ルーティング

以下は、実際のファイルパスと割り当てられるURLの対応例です。

ファイルパス 割り当てられるURL
app/page.tsx /
app/about/page.tsx /about
app/products/list/page.tsx /products/list
app/products/[id]/page.tsx /products/123 など、[id] に任意の値が入る動的ルーティングになる

App Routerのもう1つの大きな特徴は、すべてのコンポーネントがデフォルトでServer Componentとして扱われる点です。従来のReactは「ブラウザで動くコンポーネント」を前提としていましたが、App Routerでは明示的に指定しない限りサーバ側で実行されます。「ブラウザで動かしたい」場合にだけ、ファイル冒頭に "use client" を書いてClient Componentに切り替えます。

2.3 React Server ComponentとClient Componentの違い

Server Componentは、名前の通りサーバ側で実行されるコンポーネントです。HTMLに変換された結果だけがブラウザに届き、コンポーネント自体のJavaScriptはブラウザには送信されません。一方のClient Componentは、従来のReactと同じようにブラウザ側で実行されるコンポーネントで、ファイル冒頭に "use client" ディレクティブを書いて明示します。

両者の違いを整理すると、以下の通りです。

観点 Server Component(デフォルト) Client Component
実行場所 サーバ ブラウザ
指定方法 何も書かない ファイル冒頭に "use client" と書く
バンドルサイズ 含まれない JavaScriptがブラウザに配信される
データ取得 サーバ側で直接fetchやDBアクセスができる ブラウザからfetchする
useState / useEffect 使えない 使える
ブラウザAPI(window / document 使えない 使える
イベントハンドラ(onClick など) 付けられない 付けられる

Server Componentのメリットは、ブラウザに配信するJavaScriptを減らせることと、認証情報やDB接続文字列などブラウザに露出させたくない情報をコンポーネント内で直接扱える点にあります。一方のClient Componentは、useState で状態を持ちたい、ボタンにイベントハンドラを付けたい、window などのブラウザAPIを使いたい、といったインタラクティブな要素で必要になります。

「まずServer Componentで書き、インタラクティブな要素だけをClient Componentに切り出す」というのがApp Routerの基本的な進め方です。この章の後半でも、実際にServer Componentでカウンタを作ろうとしてエラーを出し、"use client" を付けて解決する体験をします。

3. Next.js プロジェクトの作成

まずは公式のスキャフォールディングツール create-next-app を使い、Next.jsプロジェクトの雛形を生成します。空のフォルダから手作業で必要ファイルを揃えるより、公式が推奨する構成を一発で作成できるため、新規Next.jsプロジェクトはほぼ常にこのツールから始めます。

3.1 プロジェクト雛形の生成

create-next-app は対話形式でTypeScript・App Router・Tailwindなどの初期設定を1つずつ聞いてくれるスキャフォールディングツールで、コマンド1本でNext.jsが必要とするファイル一式が揃った状態のプロジェクトを作成できます。まずはこのツールを使って、以降のハンズオンの土台になる雛形を生成します。

任意の場所に my-next-app フォルダを作成し、Visual Studio Codeの「ファイル」→「フォルダーを開く」から、作成した my-next-app フォルダを開きます。以降の操作は、Visual Studio Codeのターミナルから行います。

my-next-app  ← このフォルダを作成

以下のコマンドで、npm がインストールされていることを確認します。npx は npm に同梱されているため、npm のバージョンが確認できれば npx も利用できます。

npm --version

以下のように npm のバージョンが表示されれば、インストールは確認できています。

10.x.x

バージョンが表示されない場合は、Node.jsをインストールしよう を先に実施してください。

インストールが確認できたら、以下のコマンドでNext.jsプロジェクトを、いま開いているカレントフォルダに生成します。

npx create-next-app@latest .

npx はローカルにインストールされていないコマンドをその場でダウンロードして実行するNode.js標準のツールで、create-next-app@latest の部分でNext.js公式スキャフォールディングツールの最新版を実行しています。直後の . は「カレントフォルダ(いま開いている my-next-app)に生成する」ことを指定しています。

実行すると、以下の質問が表示されます。

  • Would you like to use the recommended Next.js defaults?Yes, use recommended defaults を選択して Enter

Yes を選ぶと、Next.jsの推奨構成で雛形が一括生成されます。

回答が終わると、必要な依存パッケージが自動でインストールされ、いま開いている my-next-app フォルダに雛形が生成されます。以下のような実行結果が表示されれば成功です。

Success! Created my-next-app at /path/to/my-next-app

Visual Studio Codeのサイドバーには、以下のような構成が表示されます。

my-next-app/
├── app/
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── .gitignore
├── eslint.config.mjs
├── next-env.d.ts
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── README.md
└── tsconfig.json

3.2 開発サーバの起動

雛形を生成しただけではブラウザで動作を確認できないため、開発サーバを起動します。開発サーバはコードの変更を検知して自動的にリビルド・再読み込みしてくれるため、以降の変更もこのサーバを起動したまま確認できます。

Visual Studio Codeのターミナルで、以下のコマンドを実行します。

npm run dev

npm run devpackage.jsonscripts に定義された dev スクリプトを実行するコマンドです。Next.jsの雛形では、この dev スクリプトが next dev を実行するように設定されています。Next.js 16以降ではTurbopackが標準になっているため、--turbopack オプションを付けなくても自動的にTurbopackで起動します。

以下のような実行結果が表示され、Ready in ... のメッセージが出れば起動完了です。

> my-next-app@0.1.0 dev
> next dev

   ▲ Next.js 16.x.x (Turbopack)
   - Local:        http://localhost:3000
   - Network:      http://192.168.x.x:3000

 ✓ Ready in 1.2s

ブラウザで http://localhost:3000 を開くと、Next.jsのウェルカム画面が表示されます。左上に NEXT.js のロゴ、中央に To get started, edit the page.tsx file. の見出しと、Deploy Now / Documentation の2つのボタンが並んで表示されていれば、開発サーバが正常に動作しています。

💡 ポイント
開発サーバは、以降のハンズオンでも起動したままにしておきます。コードを変更・保存するたびに、ブラウザが自動的にリロードされて反映を確認できます。停止したい場合は、ターミナルで Ctrl + C を押してください。

4. プロジェクト構造の理解

雛形を動かせるようになったので、この後の作業で扱うファイル・フォルダの役割を先に把握しておきます。App Routerは「どのファイルがどのURLに対応するか」を暗黙のルールで決めているため、構造を理解しておくことがこの後の変更の見通しをよくします。

4.1 主要なフォルダ・ファイル

雛形が生成した主要なフォルダ・ファイルの役割は以下の通りです。

フォルダ / ファイル 役割
app/ App Routerが認識するページ・レイアウトを配置するフォルダ。この中のフォルダ構造がそのままURLになる
app/page.tsx ルート(/)に対応するページコンポーネントを配置する
app/layout.tsx すべてのページで共通のレイアウトを定義する。<html> <body> タグもここに書く
app/globals.css プロジェクト全体で読み込まれるグローバルCSSを配置する
public/ 画像などの静的ファイルを配置する。public/logo.png/logo.png としてブラウザからアクセスできる
next.config.ts Next.jsのビルド設定・機能フラグを記述する
package.json 依存パッケージとnpmスクリプト(dev / build / start / lint)を定義する
tsconfig.json TypeScriptのコンパイラ設定を保持する。@/* エイリアスもここで定義されている
eslint.config.mjs Next.js推奨のESLint設定を保持する
next-env.d.ts Next.jsが自動生成する型定義ファイル。手で編集しない

この後編集する app/page.tsxapp/layout.tsxapp/ フォルダの中にあり、それ以外は雛形が自動生成したビルド設定・型定義であることが把握できていれば、この見出しは完了です。

app/page.tsx の中身の確認

フォルダ構成が把握できたところで、この後何度も編集する app/page.tsxapp/layout.tsx の中身を先に見ておきます。まずは / にアクセスしたときに描画されるページファイルを開き、App Routerでのページコンポーネントの書き方を確認します。

Visual Studio Codeのエクスプローラーから app/page.tsx を開きます。

App Routerのページコンポーネントは、page.tsxexport default された関数として定義します。書き方は以下のとおりです。

export default function 関数名() {
  return (
    // ページのJSX
  );
}

以下は、雛形が生成した app/page.tsx の構造です(実際にはNext.jsロゴやリンクなどのJSXがもう少し長く続いていますが、構造としてはこの形です)。

export default function Home() {
  return (
    <div>
      {/* Next.jsのウェルカム画面のJSX */}
    </div>
  );
}

コードを解説します。

export default function Home() {

/ にアクセスしたときに描画されるページコンポーネントです。App Routerでは app/<パス>/page.tsxexport default されたコンポーネントが、そのままそのURLで描画されるというルールになっています。関数名(Home)自体はURLと無関係で、export default されている関数がそのままページ本体として扱われます。

このファイルには "use client" が書かれていません。前述の通り、App RouterではこれがServer Componentであることを意味します。試しに console.log("hello from server") をコンポーネントの中に書いて保存すると、ブラウザのコンソールではなく、npm run dev を実行しているターミナルの方にログが出力されることで、サーバ側で実行されていることが確認できます(確認後、console.log は削除して構いません)。

app/layout.tsx の中身の確認

続いて、すべてのページで共通の外枠を担当する app/layout.tsx の中身を確認します。この後の見出しでヘッダ・フッタを追加するファイルなので、初期状態でどんな構造になっているかを先に押さえておきます。

app/layout.tsx を開きます。

layout.tsx は、children プロパティを受け取ってページの外枠を定義するコンポーネントです。書き方は以下のとおりです。

export default function 関数名({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    // 外枠のJSX。中に {children} を置く
  );
}

以下は、雛形が生成した app/layout.tsx の骨格部分(Metadata定義やフォント読み込みを除く)です。

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

コードを解説します。

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {

layout.tsx は、ページの外側を囲む枠を定義するファイルです。children プロパティを受け取る形になっており、ここに各ページ(page.tsx)の中身が差し込まれます。

<html lang="en">
  <body>{children}</body>
</html>

<html> <body> タグをここに書ける点が他のフレームワークと異なります。{children} の位置に、実際のページの中身が挿入される仕組みです。

この後の「レイアウトを共通化する」の見出しで、この layout.tsx に共通ヘッダ・フッタを追加します。

app/page.tsx がURLごとのページを、app/layout.tsx がページの外側を囲む枠を担当していることが把握できていれば、この見出しは完了です。

5. ページの追加(ファイルベースルーティング)

App Routerの中心的な仕組みであるファイルベースルーティングを体験するために、実際に新しいページを追加します。まずは静的なパス(/about)、次に動的なパス(/products/[id])の順で、URLとファイルの対応関係を実際に操作しながら把握します。

/about ページの追加

App Routerで新しいページを追加するときは、app/ 配下に対応するフォルダを作り、その中に page.tsx を置きます。/about にアクセスできるようにするために、app/about/page.tsx を作成します。

Visual Studio Codeのエクスプローラーで app フォルダを右クリックし、「新しいフォルダー」を選択して about という名前でフォルダを作成します。次に、作成した about フォルダを右クリックして「新しいファイル」から page.tsx を作成します。

my-next-app/
├── app/
│   ├── about/
│   │   └── page.tsx  ← このファイルを作成
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   ├── page.module.css
│   └── page.tsx
├── public/
├── next.config.ts
├── package.json
└── tsconfig.json

作成した app/about/page.tsx に以下の内容を記述して保存します。

export default function AboutPage() {
  return (
    <div>
      <h1>About</h1>
      <p>このページはApp Routerのファイルベースルーティングで追加されました。</p>
    </div>
  );
}

保存できたら、ブラウザで http://localhost:3000/about を開きます。「About」という見出しと、追加した本文が表示されていれば、ファイルベースルーティングが動作しています。

コードを解説します。

export default function AboutPage() {

app/page.tsx と同じく、export default された関数がそのままページ本体として扱われます。app/about/ フォルダに配置したことで、URLは自動的に /about に割り当てられます。ルーティング用の設定ファイル(routes.ts のようなもの)を書く必要はありません。

/products/[id] の動的ルーティング追加

静的なパスが動くことは確認できましたが、実際のアプリケーションでは「商品ID」「ユーザID」など、URLの一部が可変になるケースが多くあります。App Routerでは、フォルダ名を [id] のように角括弧で囲むことで、その部分を動的セグメントとして扱えます。書き方は以下のとおりです。

app/
└── パス名/
    └── [セグメント名]/
        └── page.tsx     ← `/パス名/任意の値` に対応する動的ルーティング

以下は、/products/任意のID に対応する動的ルーティングを追加する例です。

Visual Studio Codeのエクスプローラーで app フォルダを右クリックし、「新しいフォルダー」から products フォルダを作成します。次に、作成した products フォルダを右クリックして、「新しいフォルダー」から [id] フォルダを作成します(角括弧を含めて [id] という名前にします)。最後に、その [id] フォルダの中に page.tsx を作成します。

my-next-app/
├── app/
│   ├── about/
│   │   └── page.tsx
│   ├── products/
│   │   └── [id]/
│   │       └── page.tsx  ← このファイルを作成
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   ├── page.module.css
│   └── page.tsx
├── public/
├── next.config.ts
├── package.json
└── tsconfig.json

動的セグメントの値は、コンポーネントに渡される params プロパティで受け取ります。書き方は以下のとおりです。

export default async function 関数名({
  params,
}: {
  params: Promise<{ セグメント名: string }>;
}) {
  const { セグメント名 } = await params;
  // 処理
}

以下は、[id] セグメントの値を受け取って画面に表示する例です。作成した app/products/[id]/page.tsx に、以下の内容を記述して保存します。

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

  return (
    <div>
      <h1>商品詳細</h1>
      <p>商品ID: {id}</p>
    </div>
  );
}

保存できたら、ブラウザで http://localhost:3000/products/123 を開きます。「商品ID: 123」と表示されていれば、動的ルーティングが動作しています。URLの数字部分を 456 などに変えると、表示される商品IDも追随して変わります。

コードを解説します。

export default async function ProductPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {

page.tsx のコンポーネントには、Next.jsから params などのpropsが自動で渡されます。App Router(Next.js 15以降)では paramsPromise として渡されるため、コンポーネントを async にして await で解決する必要があります。型定義は Promise<{ id: string }> の部分で、URLの [id] セグメントに対応する id プロパティを持つオブジェクトが解決されることを表しています。

const { id } = await params;

await paramsPromise を解決してから、id プロパティを分割代入で取り出しています。この id に、URLの [id] 部分に入っている値(例: /products/123 なら "123")が入ります。

📝 動的セグメントの命名ルール
フォルダ名の角括弧に囲んだ名前([id]id 部分)が、そのまま params オブジェクトのキー名になります。[slug] にすれば params.slug[postId] にすれば params.postId になります。この命名はコード側と一致させる必要があります。詳しくはDynamic Routes(Next.js公式ドキュメント)に記載があります。

6. レイアウトの共通化

//about/products/123 の3つのページを作りましたが、いまはそれぞれのページに独立した内容しか表示されていません。実際のアプリケーションでは、サイト全体で共通のヘッダ・フッタを表示したいケースが一般的です。App Routerでは、この共通枠を app/layout.tsx に書くことで、すべてのページに自動的に適用できます。

6.1 ルートレイアウトへのヘッダ・フッタ追加

現在の app/layout.tsx<html> <body>{children} だけの最小構成になっています。ここに共通ヘッダ・フッタを追加し、すべてのページで表示されるようにします。

app/layout.tsx を以下の内容に書き換えます。

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create-next-app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <header style={{ padding: "1rem", borderBottom: "1px solid #ddd" }}>
          <strong>My Next App</strong>
        </header>

        <main style={{ padding: "1rem" }}>{children}</main>

        <footer style={{ padding: "1rem", borderTop: "1px solid #ddd" }}>
          <small>&copy; 2026 My Next App</small>
        </footer>
      </body>
    </html>
  );
}

保存できたら、ブラウザで http://localhost:3000http://localhost:3000/abouthttp://localhost:3000/products/123 の3つのURLを開いてみます。どのページにも同じヘッダ・フッタが表示され、真ん中の内容だけがページごとに切り替わっていれば、レイアウトの共通化ができています。

コードを解説します。

<header style={{ padding: "1rem", borderBottom: "1px solid #ddd" }}>
  <strong>My Next App</strong>
</header>

<body> の直下に <header> を配置しています。layout.tsx に書いた要素はすべてのページの外側に自動で適用されるため、app/page.tsxapp/about/page.tsx を編集しなくても全ページにヘッダが表示されます。

<main style={{ padding: "1rem" }}>{children}</main>

{children} の部分に、各ページ(page.tsx)の中身が差し込まれます。ここが「レイアウトの中の可変領域」に相当します。

<footer style={{ padding: "1rem", borderTop: "1px solid #ddd" }}>
  <small>&copy; 2026 My Next App</small>
</footer>

{children} の後にフッタを配置しています。ヘッダと同じくすべてのページで自動的に表示されます。

なお、この章ではスタイルの当て方は本題ではないため、style={{ ... }} によるインラインスタイルで済ませています。CSS ModulesやTailwind CSSといった本格的なスタイリング手法は次章のスタイリングで扱います。

7. Server ComponentとClient Componentの使い分け

「Next.jsの概要」で説明した通り、App RouterではすべてのコンポーネントがデフォルトでServer Componentとして扱われます。ここでは実際にカウンタコンポーネントを作ってServer Componentのままではエラーになることを体験し、"use client" を付けてClient Componentに切り替えることで動くようになる流れを確認します。

7.1 Server Componentでのカウンタ実装(エラーの体験)

前章の Reactの基本 で扱った useState を使ったカウンタを、App Routerのページとして作ってみます。Reactの基本章で書いたカウンタと本質的には同じコードですが、Server Component のまま("use client" を付けずに)書くとどうなるかを確認するのがここの目的です。

Visual Studio Codeのエクスプローラーで app フォルダを右クリックし、「新しいフォルダー」から counter フォルダを作成します。作成した counter フォルダを右クリックして、「新しいファイル」から page.tsx を作成します。

my-next-app/
├── app/
│   ├── about/
│   │   └── page.tsx
│   ├── counter/
│   │   └── page.tsx  ← このファイルを作成
│   ├── products/
│   │   └── [id]/
│   │       └── page.tsx
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   ├── page.module.css
│   └── page.tsx
├── public/
├── next.config.ts
├── package.json
└── tsconfig.json

作成した app/counter/page.tsx に、Server Componentのまま("use client" を書かずに)以下の内容を記述して保存します。

import { useState } from "react";

export default function CounterPage() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>カウンタ</h1>
      <p>現在の値: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1する</button>
    </div>
  );
}

保存できたら、ブラウザで http://localhost:3000/counter を開いてみます。以下のようなエラーがブラウザとターミナルの両方に表示されます。

You're importing a module that depends on `useState` into a React Server Component module. This API is only available in Client Components. To fix, mark the file (or its parent) with the `"use client"` directive.

これはApp RouterがこのファイルをServer Componentとして解釈しようとした結果、サーバ側では動かせない useState が使われていることを検知して出しているエラーです。同様に、onClick のようなイベントハンドラや、useEffect などのReact Hooks、window document などのブラウザAPIも、Server Componentでは扱えません。

"use client" によるClient Componentへの切り替え

エラーで表示された通り、このコンポーネントはブラウザ側で動く必要があるため、Client Componentに切り替える必要があります。App Routerでは、ファイルの一番上に "use client" と書くだけでClient Componentになります。書き方は以下のとおりです。

"use client";

import 〜;

// コンポーネント定義

以下は、先ほどのカウンタコンポーネントに "use client" を追加した例です。app/counter/page.tsx を、以下の内容に置き換えます。

"use client";

import { useState } from "react";

export default function CounterPage() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>カウンタ</h1>
      <p>現在の値: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1する</button>
    </div>
  );
}

保存できたら、ブラウザで http://localhost:3000/counter をリロードします。エラーが消えてカウンタが表示され、「+1する」ボタンを押すたびに数字が増えていけば、Client Componentへの切り替えは成功です。

コードを解説します。

"use client";

追加したのはファイル冒頭の1行だけです。この "use client" ディレクティブがそのファイル以下をClient Componentとして扱う目印になります。ファイルの先頭に書く必要があり、import 文よりも上に置きます。

💡 ポイント
"use client" はファイル単位で有効です。1つのページの中でも、動的な要素だけを別ファイルの小さなClient Componentに切り出し、それ以外はServer Componentのまま残す構成が推奨されます。すべてを "use client" にすると、Server Componentのメリット(バンドルサイズ削減・サーバでの直接データ取得)が失われるため、Client Componentは必要な箇所に限定します。詳しい使い分けの考え方はServer and Client Components(Next.js公式ドキュメント)に記載があります。

ページを複数作れたので、最後にページ間を移動できるようにします。通常のHTMLで書けば <a href="/about"> のようなアンカータグを使いますが、Next.jsでは代わりに next/linkLink コンポーネント を使います。ここでは両者の違いを実感するために、Link でナビゲーションを実装し、通常の <a> タグとの挙動の差を確認します。

Link でのナビゲーション追加

先ほど作成した app/layout.tsx のヘッダに、//about/counter へのリンクを追加します。ヘッダに置くことで、どのページからでもナビゲーションが表示される状態になります。

next/linkLink コンポーネントは、href に遷移先のパスを指定して使います。書き方は以下のとおりです。

import Link from "next/link";

<Link href="遷移先のパス">リンクテキスト</Link>

以下は、//about/counter へのリンクをヘッダに追加した例です。app/layout.tsx を、以下の内容にまるごと置き換えます。

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import Link from "next/link";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create-next-app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="ja">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <header style={{ padding: "1rem", borderBottom: "1px solid #ddd" }}>
          <strong style={{ marginRight: "1rem" }}>My Next App</strong>
          <Link href="/" style={{ marginRight: "1rem" }}>ホーム</Link>
          <Link href="/about" style={{ marginRight: "1rem" }}>About</Link>
          <Link href="/counter">カウンタ</Link>
        </header>

        <main style={{ padding: "1rem" }}>{children}</main>

        <footer style={{ padding: "1rem", borderTop: "1px solid #ddd" }}>
          <small>&copy; 2026 My Next App</small>
        </footer>
      </body>
    </html>
  );
}

保存できたら、ブラウザで http://localhost:3000 を開きます。ヘッダに「ホーム」「About」「カウンタ」のリンクが並んでいれば、ナビゲーションの追加は完了です。

コードを解説します。

import Link from "next/link";

Next.js標準の Link コンポーネントを読み込みます。next/link はNext.jsをインストールした時点で利用可能で、追加のインストールは不要です。

<Link href="/" style={{ marginRight: "1rem" }}>ホーム</Link>
<Link href="/about" style={{ marginRight: "1rem" }}>About</Link>
<Link href="/counter">カウンタ</Link>

href に遷移先のパスを指定します。書き方は通常の <a> タグとほぼ同じですが、生成されるHTMLに加えてクライアントサイド遷移プリフェッチが自動で有効になります。

Link<a> タグの違いの確認

Link に置き換えると具体的に何が変わるかを、ブラウザの開発者ツールを開きながら確認します。

まず、ブラウザで開発者ツールを開き(Windowsは F12、Macは Cmd + Option + I)、Networkタブを表示した状態にします。ヘッダの「About」リンクをクリックしてみると、about へのHTMLリクエストが発生せず(あるいは非常に小さなJSONのようなリクエストだけが飛び)、ページ全体のリロードが起こらないまま画面が切り替わることが確認できます。これはLink がJavaScriptによるクライアントサイド遷移を行っており、既存のReactアプリケーションを保持したまま必要な部分だけ差し替えているためです。

続いて、<a href="/counter">カウンタ</a> のように、Link を通常の <a> タグに書き換えて同じ操作をしてみると、counter に対するHTMLリクエストが発生し、ページ全体のフルリロードが起こる違いが確認できます(比較後は元の Link に戻しておいてください)。

もう1つの Link の重要な特徴がプリフェッチです。開発サーバの起動中、ブラウザの表示領域内にある Link は、ユーザがクリックする前にNext.jsが自動でリンク先のコードを裏側で先読みします。これによって、実際にクリックされたときにはすでにコードが揃っていて、ほぼ待ち時間なく画面が切り替わる、という体験を生みます。

📝 <a> タグを使うべきケース
外部サイトへのリンク(例: https://example.com/)は、Link ではなく通常の <a> タグを使います。Link はあくまで同じNext.jsアプリケーション内のページ遷移を高速化する仕組みで、外部サイトに対しては通常のブラウザ遷移が必要になるためです。詳しい仕様はLinking and Navigating(Next.js公式ドキュメント)に記載があります。

ヘッダのリンクを行き来してもフルリロードせずに画面が切り替わり、それぞれのページの内容が正しく表示されれば、Link コンポーネントによる画面遷移が正しく動作しています。

9. まとめ

この章では、Next.jsのApp Routerを学びつつ、実際にコードで基本的なページ作成・ルーティング・レンダリング切り替え・ナビゲーションを確かめました。

  • create-next-app により、App Router版のNext.jsプロジェクトを作成し、開発サーバでウェルカム画面を表示できる
  • app/ フォルダの構造がそのままURLになるファイルベースルーティングと、[id] を使った動的ルーティングを実装できる
  • app/layout.tsx にヘッダ・フッタを書くことで、すべてのページに共通のレイアウトを適用できる
  • Server Componentのまま useState を使うとエラーになり、"use client" を付けてClient Componentに切り替えることで動作させられる
  • next/linkLink コンポーネントにより、フルリロードなしのクライアントサイド遷移とプリフェッチによる画面遷移を実装できる

次の章では、スタイリングを本格的に扱い、CSS ModulesとTailwind CSSそれぞれの考え方と使い分けを学びつつ、実際にコードで扱いを確かめます。

この教材は役に立ちましたか?

いいねをたくさんいただけると、制作者の励みになり、より多くのセクションが作れるようになります。

感想を一言(任意)

いただいたコメントは次の制作のヒントになります。ぜひお気軽にご投稿ください。

このコメントは他の受講生には公開されません。DevOps Camp運営が、教材改善のために確認します。

0 / 2000