mcpField
N
nextjs-app-router

nextjs-app-router

github.com/giuseppe-trisciuoglio開発3653.9k 回インストール7 週間前に更新
リポジトリ

nextjs-app-router について

Next.js 16+ の App Router で React アプリを開発するためのスキルです。サーバー/クライアントコンポーネント、アクション、ルートハンドラー、キャッシュ、ルーティング、非同期 API を扱い、Next.js アプリの作成や保守を支援します。

主な機能

  1. 01サーバー/クライアントコンポーネントのパターン
  2. 02Server Actions とフォーム更新
  3. 03API エンドポイント用ルートハンドラー
  4. 04明示的なキャッシュと再検証
  5. 05並列ルートとインターセプトルート
  6. 06Next.js 16 の非同期 API ガイド

活用シーン

  1. 01App Router プロジェクトのセットアップ
  2. 02Server Actions を使ったフォーム構築
  3. 03ルートハンドラーによる API 作成
  4. 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.md
best-practices.md
caching-strategies.md
data-fetching.md
examples.md
metadata-api.md
nextjs16-migration.md
patterns.md
routing-patterns.md
server-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.tsxSuspense の読み込み表示"use cache"明示的なキャッシュ(Next.js 16)
error.tsxエラーバウンダリー
not-found.tsx404 ページ
route.tsAPI 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 をハードコーディングせず、環境変数を使用してください

参考資料

コメント