nextjs-app-router について
Next.js 16+ の App Router で React アプリを開発するためのスキルです。サーバー/クライアントコンポーネント、アクション、ルートハンドラー、キャッシュ、ルーティング、非同期 API を扱い、Next.js アプリの作成や保守を支援します。
主な機能
- 01サーバー/クライアントコンポーネントのパターン
- 02Server Actions とフォーム更新
- 03API エンドポイント用ルートハンドラー
- 04明示的なキャッシュと再検証
- 05並列ルートとインターセプトルート
- 06Next.js 16 の非同期 API ガイド
活用シーン
- 01App Router プロジェクトのセットアップ
- 02Server Actions を使ったフォーム構築
- 03ルートハンドラーによる API 作成
- 04キャッシュページと並列ダッシュボードルートの追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nextjs-app-routerインストール後、Claude Code で/nextjs-app-routerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/app-router-fundamentals.mdbest-practices.mdcaching-strategies.mddata-fetching.mdexamples.mdmetadata-api.mdnextjs16-migration.mdpatterns.mdrouting-patterns.mdserver-actions.md詳細
Next.js App Router(Next.js 16+)
App Router アーキテクチャを使用して、Next.js 16+ でモダンな React アプリケーションを構築します。
概要
このスキルでは、Server Components(デフォルト)と Client Components("use client")、ミューテーションやフォーム処理のための Server Actions、API エンドポイント用の Route Handlers、"use cache" ディレクティブによる明示的なキャッシュ、並列ルートとインターセプトルート、Next.js 16 の非同期 API および proxy.ts のパターンを紹介します。
使用するタイミング
次のようなリクエストがあった場合に有効化します。
- 「Next.js 16 プロジェクトを作成」「App Router をセットアップ」
- 「Server Component」「Client Component」「use client」
- 「Server Action」「フォーム送信」「ミューテーション」
- 「Route Handler」「API エンドポイント」「route.ts」
- 「use cache」「cacheLife」「cacheTag」「再検証」
- 「並列ルート」「
@スロット」「インターセプトルート」 - 「proxy.ts」「middleware.ts から移行」
- 「layout.tsx」「page.tsx」「loading.tsx」「error.tsx」「not-found.tsx」
- 「generateMetadata」「next/image」「next/font」
クイックリファレンス
| ファイル | 目的 | ディレクティブ | 目的 |
|---|---|---|---|
page.tsx | ルートページ | "use server" | Server Action 関数 |
layout.tsx | 共通レイアウト | "use client" | Client Component の境界 |
loading.tsx | Suspense の読み込み表示 | "use cache" | 明示的なキャッシュ(Next.js 16) |
error.tsx | エラーバウンダリー | ||
not-found.tsx | 404 ページ | ||
route.ts | API Route Handler | ||
proxy.ts | ルーティング境界 |
手順
新しいプロジェクトを作成する
npx create-next-app@latest my-app --typescript --tailwind --app --turbopack
Server Component
Server Components は App Router のデフォルトです。サーバー上で実行され、async/await を使用できます。
// app/users/page.tsx
async function getUsers() {
const apiUrl = process.env.API_URL;
const res = await fetch(`${apiUrl}/users`);
return res.json();
}
export default async function UsersPage() {
const users = await getUsers();
return <main>{users.map(user => <UserCard key={user.id} user={user} />)}</main>;
}
Client Component
フック、ブラウザー API、またはイベントハンドラーを使用する場合は、"use client" を追加します。
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
Server Action
"use server" ディレクティブを使って、アクションを別ファイルに定義します。
// app/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createUser(formData: FormData) {
const name = formData.get("name") as string;
const email = formData.get("email") as string;
await db.user.create({ data: { name, email } });
revalidatePath("/users");
}
Client Components のフォームで使用します。
"use client";
import { useActionState } from "react";
import { createUser } from "./actions";
export default function UserForm() {
const [state, formAction, pending] = useActionState(createUser, {});
return (
<form action={formAction}>
<input name="name" />
<input name="email" type="email" />
<button type="submit" disabled={pending}>{pending ? "Creating..." : "Create"}</button>
</form>
);
}
Zod による検証、楽観的更新、高度なパターンについては、references/server-actions.md を参照してください。
キャッシュを設定する
明示的なキャッシュには "use cache" ディレクティブを使用します(Next.js 16+)。
"use cache";
import { cacheLife, cacheTag } from "next/cache";
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
cacheTag(`product-${id}`);
cacheLife("hours");
const product = await fetchProduct(id);
return <ProductDetail product={product} />;
}
キャッシュプロファイル、オンデマンド再検証、高度なパターンについては、references/caching-strategies.md を参照してください。
Route Handler
// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
return NextResponse.json(await db.user.findMany());
}
export async function POST(request: NextRequest) {
const body = await request.json();
return NextResponse.json(await db.user.create({ data: body }), { status: 201 });
}
動的セグメントには [param] を使用します。
// app/api/users/[id]/route.ts
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const user = await db.user.findUnique({ where: { id } });
if (!user) return NextResponse.json({ error: "Not found" }, { status: 404 });
return NextResponse.json(user);
}
Next.js 16 の非同期 API
バージョン 16 では、すべての Next.js API が非同期です。
import { cookies, headers } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
const session = cookieStore.get("session")?.value;
const userAgent = headersList.get("user-agent");
return <div>...</div>;
}
params と searchParams も Promise ベースです。
export default async function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>;
searchParams: Promise<{ sort?: string }>;
}) {
const { slug } = await params;
const { sort } = await searchParams;
// ...
}
移行ガイドと proxy.ts の設定については、references/nextjs16-migration.md を参照してください。
並列ルート
並列ルートのスロットには @folder 規約を使用します。
// app/dashboard/layout.tsx
export default function DashboardLayout({ children, team, analytics }: Record<string, React.ReactNode>) {
return (
<div>
{children}
<div className="grid grid-cols-2">{team}{analytics}</div>
</div>
);
}
// app/dashboard/@team/page.tsx
export default function TeamPage() { return <div>Team Section</div>; }
// app/dashboard/@analytics/page.tsx
export default function AnalyticsPage() { return <div>Analytics Section</div>; }
インターセプトルート、ルートグループ、動的ルートについては、references/routing-patterns.md を参照してください。
ベストプラクティス
サーバーとクライアントの選択:
- Server Component(デフォルト)から始めます
- Client Component を使用するのは、React フック(useState、useEffect)、ブラウザー API(window、document)、イベントハンドラー(onClick、onSubmit)、またはクライアント専用ライブラリが必要な場合に限ります
データ取得:
- 可能な限り Server Components でデータを取得します
- 重複排除には React の
cache()を使用します - 独立したデータ取得は並列化します
loading.tsxで Suspense 境界を追加します
パフォーマンスのチェックリスト:
- Suspense 境界には
loading.tsxを使用します - 画像の最適化には
next/imageを使用します - フォントの最適化には
next/fontを使用します - エラー処理のために
error.tsxとnot-found.tsxを追加します
例
例 1:Server Action を使ったブログ投稿フォーム
入力: Zod による検証を行うブログ投稿送信用フォームを作成する
出力:
// app/blog/actions.ts
"use server";
import { z } from "zod";
import { revalidatePath } from "next/cache";
const schema = z.object({ title: z.string().min(5), content: z.string().min(10) });
export async function createPost(formData: FormData) {
const parsed = schema.safeParse({ title: formData.get("title"), content: formData.get("content") });
if (!parsed.success) return { errors: parsed.error.flatten().fieldErrors };
await db.post.create({ data: parsed.data });
revalidatePath("/blog");
return { success: true };
}
// app/blog/new/page.tsx
"use client";
import { useActionState } from "react";
import { createPost } from "../actions";
export default function NewPostPage() {
const [state, formAction, pending] = useActionState(createPost, {});
return (
<form action={formAction}>
<input name="title" placeholder="Title" />
{state.errors?.title && <span>{state.errors.title[0]}</span>}
<textarea name="content" placeholder="Content" />
{state.errors?.content && <span>{state.errors.content[0]}</span>}
<button type="submit" disabled={pending}>{pending ? "Publishing..." : "Publish"}</button>
</form>
);
}
例 2:キャッシュされた商品ページ
入力: オンデマンド再検証に対応した、キャッシュされる商品ページを作成する
出力:
// app/products/[id]/page.tsx
"use cache";
import { cacheLife, cacheTag } from "next/cache";
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
cacheTag(`product-${id}`, "products");
cacheLife("hours");
const product = await db.product.findUnique({ where: { id } });
if (!product) notFound();
return <article><h1>{product.name}</h1><p>{product.description}</p></article>;
}
// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const { tag } = await request.json();
revalidateTag(tag);
return NextResponse.json({ revalidated: true });
}
例 3:並列ルートを使ったダッシュボード
入力: サイドバーと統計エリアを備えたダッシュボードを作成する
出力:
// app/dashboard/layout.tsx
export default function DashboardLayout({ children, sidebar, stats }: Record<string, React.ReactNode>) {
return (
<div className="flex">
<aside className="w-64">{sidebar}</aside>
<main className="flex-1"><div className="grid grid-cols-3">{stats}</div>{children}</main>
</div>
);
}
// app/dashboard/@sidebar/page.tsx
export default function Sidebar() { return <nav>{/* Navigation links */}</nav>; }
// app/dashboard/@stats/page.tsx
export default async function Stats() {
const stats = await fetchStats();
return <><div>Users: {stats.users}</div><div>Orders: {stats.orders}</div></>;
}
制約と警告
制約:
- サーバーコンポーネントではブラウザー API や React フックを使用できません
- クライアントコンポーネントを非同期にすることはできません(データを直接取得できません)
- Next.js 16 では
cookies()、headers()、draftMode()は非同期です - Next.js 16 では
paramsとsearchParamsは Promise ベースです - Server Actions は
"use server"ディレクティブを付けて定義する必要があります
警告:
- クライアントコンポーネントで
awaitを使用すると、ビルドエラーが発生します - サーバーコンポーネントで
windowやdocumentにアクセスすると、エラーが発生します - Next.js 16 で
cookies()またはheaders()のawaitを忘れると、値ではなく Promise が返されます - 適切な検証を行わずに Server Actions を使用すると、データベースが不正アクセスにさらされるおそれがあります
- 外部データの取得:
fetch()でサードパーティの URL を取得するサーバーコンポーネントは、信頼できないコンテンツを処理します。レスポンスは必ず検証、サニタイズ、型チェックを行ってください。また、API の URL をハードコーディングせず、環境変数を使用してください
参考資料
- references/app-router-fundamentals.md — サーバー/クライアントコンポーネント、ファイル規約、ナビゲーション
- references/routing-patterns.md — 並列ルート、インターセプトルート、ルートグループ
- references/caching-strategies.md — "use cache"、cacheLife、cacheTag、再検証
- references/server-actions.md — Server Actions、useActionState、検証、楽観的更新
- references/nextjs16-migration.md — 非同期 API、proxy.ts、Turbopack、設定
- references/data-fetching.md — データ取得パターン、Suspense、ストリーミング
- references/metadata-api.md — generateMetadata、OpenGraph、サイトマップ