mcpField
N
nextjs-mastery

nextjs-mastery

github.com/rohitg00開発2.8k190 回インストール24 週間前に更新
リポジトリ

nextjs-mastery について

App Router で Next.js アプリを構築する開発者向けの実践的なリファレンススキルです。サーバー側のデータ処理、キャッシュ、ルーティング、実装上の注意点を扱います。

主な機能

  1. 01App Router の構成
  2. 02Server Components とデータ取得
  3. 03ISR とキャッシュ再検証
  4. 04Middleware と Server Actions
  5. 05実装チェックリストとアンチパターン

活用シーン

  1. 01ルートとレイアウトの設計
  2. 02サーバー側データ取得方法の選択
  3. 03キャッシュと更新後の再検証の追加
  4. 04Next.js 実装の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rohitg00/awesome-claude-code-toolkit --skill nextjs-mastery

インストール後、Claude Code で/nextjs-masteryを入力して読み込まれたか確認します。

詳細

Next.js を極める

App Router の構成

app/
  layout.tsx              # Root layout (wraps all pages)
  page.tsx                # Home route /
  loading.tsx             # Route-level Suspense fallback
  error.tsx               # Route-level error boundary
  not-found.tsx           # Custom 404
  (marketing)/
    about/page.tsx        # /about (grouped without URL segment)
  dashboard/
    layout.tsx            # Nested layout for /dashboard/*
    page.tsx              # /dashboard
    @analytics/page.tsx   # Parallel route slot
    @activity/page.tsx    # Parallel route slot
    settings/
      page.tsx            # /dashboard/settings
  api/
    webhooks/route.ts     # Route handler (POST /api/webhooks)

ルートグループ (name) は、URL に影響を与えずにコードを整理します。パラレルルート @slot は複数のページを同時にレンダリングします。

サーバーコンポーネントとデータ取得

async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const product = await db.product.findUnique({ where: { id } });

  if (!product) notFound();

  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews productId={id} />
      </Suspense>
    </div>
  );
}

async function Reviews({ productId }: { productId: string }) {
  const reviews = await db.review.findMany({ where: { productId } });
  return (
    <ul>
      {reviews.map(r => <li key={r.id}>{r.text} - {r.rating}/5</li>)}
    </ul>
  );
}

サーバーコンポーネントがデフォルトです。サーバー上で実行され、データベースに直接アクセスでき、クライアントに JavaScript を一切送信しません。

ISR とキャッシュ

export const revalidate = 3600;

async function BlogPage() {
  const posts = await fetch("https://api.example.com/posts", {
    next: { revalidate: 3600, tags: ["posts"] },
  }).then(r => r.json());

  return <PostList posts={posts} />;
}
import { revalidateTag, revalidatePath } from "next/cache";

export async function createPost(formData: FormData) {
  "use server";
  await db.post.create({ data: { title: formData.get("title") as string } });
  revalidateTag("posts");
  revalidatePath("/blog");
}

ミドルウェア

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("session")?.value;

  if (request.nextUrl.pathname.startsWith("/dashboard") && !token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  const response = NextResponse.next();
  response.headers.set("x-request-id", crypto.randomUUID());
  return response;
}

export const config = {
  matcher: ["/dashboard/:path*", "/api/:path*"],
};

ミドルウェアは、マッチしたすべてのリクエストの前にエッジで実行されます。軽量に保ってください。

サーバーアクション

"use server";

import { z } from "zod";
import { revalidatePath } from "next/cache";

const schema = z.object({
  email: z.string().email(),
  name: z.string().min(2).max(100),
});

export async function updateProfile(prevState: any, formData: FormData) {
  const parsed = schema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) {
    return { errors: parsed.error.flatten().fieldErrors };
  }

  await db.user.update({
    where: { email: parsed.data.email },
    data: { name: parsed.data.name },
  });

  revalidatePath("/profile");
  return { success: true };
}

アンチパターン

  • 最上位のレイアウトやページのコンポーネントに 'use client' を追加する
  • サーバーコンポーネントで取得できるデータをクライアントで取得する
  • サーバーコンポーネントや use() の代わりに、データ取得に useEffect を使う
  • 処理に時間のかかる非同期コンポーネントを <Suspense> で囲まない
  • ミドルウェアに重いロジックを置く(マッチしたすべてのリクエストで実行されます)
  • loading.tsx と error.tsx の規約を無視する

チェックリスト

  • サーバーコンポーネントをデフォルトで使用し、'use client' は操作可能な末端のコンポーネントにのみ使用する
  • データ取得を適切なキャッシュ設定とともにサーバーコンポーネントで行う
  • 独立した非同期セクションを <Suspense> 境界で囲む
  • 主要なルートに loading.tsx と error.tsx がある
  • ミドルウェアを軽量に保ち、認証、リダイレクト、ヘッダーの処理のみを行う
  • データベースへの書き込み前に、サーバーアクションで Zod を使って入力を検証する
  • ミューテーション後に revalidateTag または revalidatePath を呼び出す
  • 複雑なレイアウトの整理にルートグループとパラレルルートを使用する

コメント