React・Next.jsプロジェクトのAIコーディングルールは他のフレームワークより複雑です。正しいパターンがコンテキストに強く依存するからです。コンポーネントをサーバーコンポーネントにするかクライアントコンポーネントにするか、データ取得をServer ActionとAPIルートのどちらに書くか、状態をReactのstateに置くかURLのsearch paramに置くか。明確なルールなしにAIアシスタントを使うと、これらの判断がセッションごとに変わり、パフォーマンスへの影響が大きい選択を誤ることもあります。
本記事ではReact・Next.js 14+(App Router)向けのCLAUDE.mdテンプレートと.cursorrulesの実例を提供します。実際のプロジェクトのReact/Next.js向けルールはギャラリーで確認できます。
最重要ルール:サーバーコンポーネント vs クライアントコンポーネント
現代のNext.jsプロジェクトでAIツールが最も判断を誤るのがここです。デフォルトの答えは「サーバーコンポーネント」で、例外は明確に定義します:
## Reactコンポーネントモデル(Next.js App Router)
### デフォルト:サーバーコンポーネント
全コンポーネントはデフォルトでサーバーコンポーネント。以下の条件が当てはまらない限り
"use client"を追加しないこと:
- Reactフックを使う(useState、useEffect、useReducer等)
- ブラウザAPIを使う(window、document、localStorage等)
- インタラクティブなユーザーイベントを扱う(状態を伴うonChange、onClick等)
- リアルタイム更新が必要(useWebSocket等)
### "use client"を追加する場合
必要な場合のみファイルの先頭に"use client"を追加する。
ツリーの可能な限り末端に"use client"を押し下げる — 親コンポーネントは
サーバーコンポーネントとして保ち、末端コンポーネントのみクライアントコンポーネントにする。
### サーバーコンポーネントのパターン
- データ取得はサーバーコンポーネントで行う(async/awaitを直接使う)
- DBクエリは直接サーバーコンポーネントに書く
- サーバーサイドの呼び出しに使うAPIキーはブラウザに渡さない
### クライアントコンポーネントのパターン
- インタラクティブなフォーム(バリデーション状態を持つ)
- ドロップダウン、モーダル、ツールチップ
- チャートや可視化(ほとんどのチャートライブラリはDOMが必要)
- サードパーティのフックを使うコンポーネント
このルールにより、安全策として全コンポーネントに”use client”を追加するという典型的なAIの動作を防げます。
TypeScriptの設定
## TypeScriptルール
- strictモード有効(tsconfig.json: "strict": true)
- `any`型は禁止 — 型が本当に不明な場合は`unknown`を使う
- 非nullアサーション(!)は原則禁止(使う場合は理由をコメントで説明)
- リテラルチェックが必要な型付きオブジェクトには`satisfies`演算子を使う
- コンポーネントのprops: interfaceで定義(判別可能なユニオンが必要な場合を除く)
## コンポーネントの型パターン
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "ghost";
disabled?: boolean;
}
## 型インポート
- 型のみのインポートには必ず`import type`を使う
- import type { User } from "@/types/user";
- import { createUser } from "@/lib/db"; // 値のインポート
## パスエイリアス
- src/ルートには@/を使う
- @/components、@/lib、@/types、@/hooks
- ../../..のような相対インポートは禁止
データ取得パターン
App Routerはデータ取得を大きく変えました:
## データ取得(App Router)
### サーバーコンポーネント — async/awaitを直接使う
async function ProductPage({ params }: { params: { id: string } }) {
const product = await db.products.findUnique({ where: { id: params.id } });
if (!product) notFound();
return <ProductDetail product={product} />;
}
### Server Actions — フォームのミューテーション用
"use server";
export async function createProduct(formData: FormData) {
const name = formData.get("name") as string;
await db.products.create({ data: { name } });
revalidatePath("/products");
}
### APIルート — 実際のAPIが必要な場合のみ
// Webhook、OAuthコールバック、サードパーティ統合にのみ使う
// サーバーコンポーネントのデータ取得目的でAPIルートを作らない
### キャッシュの動作
// App Routerではfetch()はデフォルトでキャッシュされる
// 動的データには{ cache: "no-store" }を追加
// ISR動作には{ next: { revalidate: 3600 } }を追加
フォームとバリデーション
## フォーム
### プログレッシブエンハンスメント付きServer Actions
// フォームにはServer Actionsを優先 — JavaScriptなしでも動作する
"use server";
import { z } from "zod";
const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
});
export async function createUser(prevState: unknown, formData: FormData) {
const validated = CreateUserSchema.safeParse({
name: formData.get("name"),
email: formData.get("email"),
});
if (!validated.success) {
return { error: validated.error.flatten() };
}
await db.users.create({ data: validated.data });
redirect("/users");
}
### バリデーション
- スキーマバリデーションにZodを使う(yupやjoiは使わない)
- クライアントだけでなくサーバーでもバリデーションを行う
- 複雑なクライアントサイドのフォーム状態が必要な場合のみreact-hook-formを使う
スタイリングルール
## スタイリング
- 全スタイリングにTailwind CSSを使う(CSSモジュール・styled-componentsは禁止)
- UIコンポーネントにshadcn/ui(/components/ui/に配置)
- コンポーネントのバリアントにcva()(class-variance-authority)
- レスポンシブ: モバイルファースト(sm:、md:、lg:プレフィックス)
## Tailwindの規約
- カスタムCSSよりユーティリティクラスを優先
- 繰り返すパターンはコンポーネント化する(@apply directiveは禁止)
- ダークモード: クラスベース(dark:)、メディアクエリは使わない
- 動的な値・アニメーション以外でinline style={}は禁止
## shadcn/uiの使い方
// ライブラリから直接ではなく@/components/ui/からインポート
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
テストルール
## テストセットアップ
- ユニットテストにVitest(JestではなくVitest — Vite/Next.jsでより高速)
- コンポーネントテストに@testing-library/react
- E2EテストにPlaywright
- テストコマンド: npx vitest run
- E2Eコマンド: npx playwright test
## テスト対象
- Server Actions: 関数を直接テスト(ただの非同期関数)
- サーバーコンポーネント: React Testing Libraryまたは直接レンダー
- クライアントコンポーネント: @testing-library/user-eventでインタラクションをテスト
ファイル構造
## ファイル構造(App Router)
src/
app/ # Next.js App Router
layout.tsx
page.tsx
[feature]/
page.tsx # フィーチャーページ(サーバーコンポーネント)
loading.tsx
error.tsx
components/
ui/ # shadcn/uiコンポーネント(自動生成)
[feature]/ # フィーチャー固有のコンポーネント
shared/ # 共有コンポーネント
lib/
db.ts # DBクライアント
auth.ts # 認証ユーティリティ
actions/ # Server Actions
types/ # TypeScript型定義
hooks/ # カスタムReactフック(クライアントサイド)
## 命名規約
- ページ: 小文字とダッシュ(app/user-settings/page.tsx)
- コンポーネント: PascalCase(UserProfile.tsx)
- フック: "use"プレフィックス付きcamelCase(useUserProfile.ts)
- Server Actions: camelCase動詞(createUser、deleteProduct)
Next.jsプロジェクト向け完全CLAUDE.mdテンプレート
# Next.jsプロジェクト — Claude Code指示
## スタック
- Next.js 15(App Router)
- TypeScript strictモード
- Tailwind CSS + shadcn/ui
- Prisma + PostgreSQL
- ミューテーションにServer Actions
- Vitest + Playwright
## コンポーネントルール
- デフォルトはサーバーコンポーネント("use client"なし)
- "use client"はフック・ブラウザAPI・インタラクティブイベントハンドラが必要な場合のみ
- クライアントコンポーネントはツリーの末端に押し下げる
## データ取得
- サーバーコンポーネントでasync/awaitを直接使う
- フォームのミューテーションにServer Actions(APIルートではなく)
- クライアントサイドのリアルタイムデータにSWR
## TypeScript
- `any`禁止(`unknown`を使う)
- コメントなしの非nullアサーション禁止
- 型のみのインポートには`import type`
- @/パスエイリアス
## スタイリング
- Tailwindユーティリティのみ(CSSモジュール禁止)
- @/components/ui/からshadcn/ui
## テスト
- Vitest: npx vitest run
- E2E: npx playwright test
- Server Actionsは直接テスト
## 禁止事項
- レイアウトやページファイルへの"use client"(強い理由なしに)
- サーバーコンポーネントで取得できるデータへのAPIルート
- クライアントコンポーネントからの直接DB呼び出し
React・Next.js向けのAIルールの実例はギャラリーで確認できます。PythonプロジェクトのルールについてはPythonコーディングルールガイドも参照してください。