5.3 Next.js App Router 入門
5.1 と 5.2 で見た RSC を、実務で実際に動かすためのフレームワークが Next.js です。Next.js には 2 種類のルーターがあり、 RSC を前提にしているのは App Router(app/ ディレクトリ)のほうです。もう 1 つの Pages Router(pages/ ディレクトリ)は従来の SPA スタイルで、 RSC は使えません。案件で新規に Next.js を触ると App Router 側になることが多いので、 この章では App Router に絞って構造を見ていきます。
公式チュートリアルの代わりではなく、 案件でコードを読んだときに「あ、これか」と分かるための地図 を作るのが、この章の目的です。詳細は nextjs.org/docs を参照してください。
5.3.1 ディレクトリがそのままルーティングになる
App Router は ファイルシステムベースのルーティング です。app/ 以下のフォルダ名が URL のパスになり、フォルダの中の決まったファイル名(page.tsx など)が画面を決めます。
図 5.3.1 app/ とルートの対応
「フォルダ = ルートセグメント、ファイル = そのセグメントの UI」 という対応になっています。公式の言い方は次の通りです。
Folders are used to define the route segments that map to URL segments. Files (like
pageandlayout) are used to create UI that is shown for a segment. (nextjs.org / Layouts and Pages)
図 5.3.2 app/ ディレクトリツリーと特殊ファイルの役割
ファイル名のうち、特別な意味を持つもの( page.tsx / layout.tsx / loading.tsx / error.tsx / not-found.tsx / route.ts )は、 置いた場所で役割が決まります。次の図で全体像をつかんでみてください。
以降の節で、 これらのファイルの中身を順に見ていきましょう。
5.3.2 覚えておく特殊ファイル
App Router にはフォルダの中で特別な意味を持つファイル名がいくつかあります。最低限これだけ知っておけば、 案件のコードはだいたい読めます。
| ファイル名 | 役割 |
|---|---|
page.tsx | そのセグメントの画面本体。default export が必要 |
layout.tsx | そのセグメント以下で共有される枠組み。children を受け取る |
loading.tsx | そのセグメントがロード中に出す UI(内部的に Suspense のフォールバック) |
error.tsx | そのセグメント以下でエラーが起きたときの UI(Error Boundary) |
not-found.tsx | 404 ページ |
route.ts | そのセグメントを HTTP ハンドラにする(GET / POST 等) |
page.tsx は必須で、 これを置いて初めてその URL が公開されます。
A
pagefile is required to make a route segment publicly accessible. (nextjs.org / page.js)
page.tsx の最小形
// app/page.tsx
export default function Page() {
return <h1>Hello Next.js!</h1>;
}layout.tsx の最小形(root layout)
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<header>ヘッダー</header>
<main>{children}</main>
</body>
</html>
);
}app/layout.tsx は root layout と呼ばれます。これは 必須 で、 <html> と <body> を書くのはここだけ というルールになっています。
The root layout is required and must contain
htmlandbodytags. (nextjs.org / Layouts and Pages)
サブディレクトリに layout.tsx を置くと、 そのセグメント以下で共有される枠になります。入れ子にもできます。
図 5.3.3 layout / page のネスト構造
/blog/hello を開くと、 root layout > blog layout > [slug] page が外側から順に入れ子になってレンダーされます。ナビゲーション時、 layout は 再レンダーされずに残る のが特徴です。
On navigation, layouts preserve state, remain interactive, and do not rerender. (nextjs.org / Layouts and Pages)
5.3.3 動的セグメント
URL の一部が可変なページ(ブログの記事や商品の詳細など)は、 フォルダ名を [ ] で囲みます。
| 書き方 | URL 例 | params |
|---|---|---|
app/blog/[slug]/page.tsx | /blog/hello | { slug: 'hello' } |
app/shop/[category]/[item]/page.tsx | /shop/shoes/42 | { category: 'shoes', item: '42' } |
app/blog/[...slug]/page.tsx | /blog/a/b/c | { slug: ['a', 'b', 'c'] } |
page.tsx の props として params を受け取ります。Next.js 15 以降、 params は Promise なので await して取り出す形になっています。
// app/blog/[slug]/page.tsx
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
);
}Since the
paramsprop is a promise, you must useasync/awaitor React'susefunction to access the values. (nextjs.org / page.js)
クエリ文字列(?page=2 のようなもの)は searchParams で受け取れます。こちらも Promise です。
5.3.4 ルートグループとスロット
もう 2 つ、 案件コードでよく見る書き方に軽く触れておきます。
- Route Groups(ルートグループ): フォルダ名を
( )で囲むと、 そのフォルダ名は URL に含まれなくなります。app/(marketing)/about/page.tsxは/aboutとして公開されます。同じ URL 構造のまま、 コード上は(marketing)/(shop)のようにグループ分けする用途で使います。 - Parallel Routes(パラレルルート / 名前付きスロット): フォルダ名を
@で始めると、 layout の props として名前付きスロットになります。app/@analytics/page.tsxは layout の props にanalyticsとして渡されます。ダッシュボードのように 1 画面に複数の独立した領域を並べるときに使われます。
いずれも Next.js Docs の File-system conventions に正式な項目として載っています(Route Groups / Parallel Routes)。この章では概要のみ触れて、 詳細は公式ドキュメントに委ねます。
5.3.5 ページ間の遷移(next/link)
画面間の移動には、 next/link の Link コンポーネントを使います。素の <a> ではなくこちらを使う理由は、 プリフェッチ と クライアントサイドトランジション が効くからです。
// app/ui/nav.tsx
import Link from 'next/link';
export default function Nav() {
return (
<nav>
<Link href="/">トップ</Link>
<Link href="/blog">ブログ</Link>
</nav>
);
}公式の説明を引いておきます。
Next.js automatically prefetches routes linked with the
<Link>component when they enter the user's viewport. (nextjs.org / Linking and Navigating)
<Link> がビューポートに入ると、 その遷移先のルートをバックグラウンドで取りに行きます。ユーザーがクリックした時点ではもうデータが手元にあることが多く、 遷移は即時に感じられます。
素の <a> を使うと、 フルページ遷移(リロード)になってしまうので、 この最適化は効かなくなります。
5.3.6 データ取得は Server Component の中で直接
SPA では useEffect の中で fetch を呼ぶのが定型パターンでした。App Router では、 Server Component の関数を async にして await するだけ で済みます。
// app/blog/page.tsx
export default async function Page() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return (
<ul>
{posts.map((p: { id: string; title: string }) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}To fetch data with the
fetchAPI, turn your component into an asynchronous function, and await thefetchcall. (nextjs.org / Fetching Data)
データベースクライアントや ORM を直接呼ぶこともできます。認証トークンなどの秘密情報も、 Server Component の中なら安全に扱えます。クライアントバンドルには含まれないからです。
キャッシュ挙動に注意(Next.js 15+)
Next.js 15 以降、 Server Component 内の fetch は デフォルトではキャッシュされず、 リクエストごとに新規取得 になります。公式ドキュメントの原文は次のとおりです。
fetchrequests are not cached by default and will block the page from rendering until the request is complete. (nextjs.org / Fetching Data)
毎リクエストごとに外部 API を叩きたくない場合は、 次のいずれかでキャッシュに乗せます。
fetch(url, { cache: 'force-cache' })— 明示的にキャッシュするfetch(url, { next: { revalidate: 60 } })— 指定秒ごとに再検証しつつキャッシュする- Next.js 16 以降の
use cacheディレクティブ — コンポーネントや関数単位でキャッシュする
詳細は Next.js Docs — Caching と fetch API リファレンス を参照してください。
遅いデータがあるときは、 loading.tsx を置くか <Suspense> で囲むことで、 画面のほかの部分を先に見せることができます(4.3 参照)。
5.3.7 <head> は metadata で
タイトル、OGP、description といった <head> 要素は、 手で <title> を書くのではなく metadata export を使います。
// app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'My Next.js Site',
description: 'React 学習サンプル',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>{children}</body>
</html>
);
}各 page.tsx や layout.tsx で個別の metadata を export すると、 外側のものを上書きできます。
You should not manually add
<head>tags such as<title>and<meta>to root layouts. Instead, you should use the Metadata API. (nextjs.org / layout.js)
5.3.8 案件で見るときのポイント
最後に、 案件の Next.js コードを読み始めるとき、 最初に見るとよい場所を並べておきます。
app/layout.tsx— root layout。<html>/<body>とアプリ全体のスタイルやプロバイダがここにまとまっていますapp/page.tsx— トップページapp/**/page.tsx— ページの実体。各ファイルの冒頭を見れば、 Server Component(ディレクティブなし、asyncで OK)か Client Component('use client')かが分かりますnext.config.js/next.config.ts— ビルド設定。リライト・リダイレクト・画像ドメインなどmiddleware.ts(またはproxy.ts系) — リクエスト前処理app/**/route.ts— HTTP ハンドラ(API エンドポイント相当)
'use client' の有無を冒頭で確認する癖を付けておくと、 「このファイルはサーバー側で動く」「ここからブラウザ側」 という切り分けがすぐに掴めるようになります。
次の章では、 フォーム送信とデータ更新 を Server Actions でどう書くかを見ていきましょう。