ReactとNext.jsのプロジェクトは、開発が進むにつれてコーディング規約が急速に積み上がっていく。数週間も経てば、ファイル命名、状態管理、スタイリング、テスト、データ取得——あらゆる場所に「このプロジェクトのやり方」が生まれる。その規約が文書化されていなければ、新しい貢献者(人間であれAIであれ)は必ずバラバラな判断をし始める。
Claude Codeはファイルに触れる前にCLAUDE.mdを読む。これがまさに、プロジェクトの規約を書き込む適切な場所だ。CLAUDE.mdに規約を定義しておけば、Claude Codeが生成するコンポーネント・APIルート・テストは、モデルのデフォルト挙動ではなく、すでにプロジェクトで使われているスタイルに合致したものになる。
このガイドでは、ReactとNext.js開発においてClaude Codeを本格的に活用するために必要なものをすべてカバーする。コピペして使えるCLAUDE.mdテンプレート、コンポーネント生成規則、TypeScriptとTailwindのパターン、VitestとPlaywrightによるテスト、チーム向けのAGENTS.md設定まで。App RouterとPages Router両方に対応し、差異がある箇所は明示して解説する。
CLAUDE.mdがないと何が起きるか
デフォルトの挙動が悪いわけではないが、プロジェクトの判断と食い違う選択をする:
interfaceとtypeを気分で使い分ける(統一規約を無視する)- stateを持つコンポーネントすべてに
"use client"を追加する(RSCの合成で解決できる場合でも) - APIルートをトレーニングデータで最後に見た形式で生成する(実際のルーターと合わない)
- Zustandに入れるべき状態に
useStateを使うか、ローカルで十分な状態にZustandを使う Button.tsx、button.tsx、Button/index.tsxのどれで命名するかをコンテキストから推測する
300行のCLAUDE.mdはこれらをすべて解決する。Claude Codeが実行するすべてのプロンプトがそのコンテキストを受け取る。AIの出力をスタイル違反でレビューする作業がなくなり、ロジックのレビューに集中できる。
App Router用 CLAUDE.mdテンプレート
Next.js 14+(App Router)、TypeScript strict mode、Tailwind CSS、Vitestに対応したテンプレート。プロジェクトのパスと依存関係を調整してプロジェクトルートにコミットする。
# Project: [アプリ名]
## スタック
- Next.js 14+(App Router)
- TypeScript 5+(strict mode)
- Tailwind CSS v3
- Vitest + Testing Library(ユニット・統合テスト)
- Playwright(E2Eテスト)
- Zustand(グローバル状態管理)
- Prisma(DBアクセス、該当する場合)
## ディレクトリ構成
src/
app/ # Next.js App Router ページ・レイアウト
(auth)/ # ルートグループ:認証ページ
(dashboard)/ # ルートグループ:認証済みアプリ
api/ # APIルートハンドラ
components/
ui/ # プリミティブ: Button, Input, Modal 等
features/ # 機能スコープのコンポーネント
layouts/ # レイアウトコンポーネント
lib/ # ユーティリティ・ヘルパー・定数
hooks/ # カスタムReactフック
stores/ # Zustandストア
types/ # 共有TypeScript型
server/ # サーバー専用ロジック(クライアントからimport禁止)
## ファイル命名
- Reactコンポーネント: PascalCase(`UserCard.tsx`)
- その他すべてのファイル: kebab-case(`use-auth.ts`、`date-utils.ts`)
- ルートハンドラ: `app/api/` 内の `route.ts`
- ページ: `page.tsx`、レイアウト: `layout.tsx`
- テストファイル: コロケーション配置 `ComponentName.test.tsx`
## コンポーネント規則
- named exportを使う。default exportは使わない
- Props interfaceは `ComponentNameProps` という名前で同じファイルに定義
- Props interfaceはコンポーネント関数の直前に配置
- Reactのimportは不要(React 17+ JSX transformが設定済み)
例:
```tsx
interface UserCardProps {
user: User;
onSelect?: (id: string) => void;
}
export function UserCard({ user, onSelect }: UserCardProps) {
// ...
}
“use client” / “use server” ルール
- デフォルト: コンポーネントはServer Component。“use client”は必要な場合のみ追加
- “use client”を追加するのはこのときだけ: useState、useEffect、useReducer、 イベントハンドラ(onClick、onChange等)、ブラウザAPI、クライアントコンテキストを 必要とするサードパーティフックを使う場合
- “use client”を追加してはいけない場面: データのみフェッチするコンポーネント、 インタラクションのないレイアウトコンポーネント、インタラクションのない純表示コンポーネント
- “use server”は、フォームやサーバーアクション経由で呼び出されるサーバー専用の非同期関数に追加。
server/配下か、ファイル先頭に"use server"ディレクティブを置いたファイルに配置 - “use client”境界はできるだけ末端(インタラクションに近い場所)に置く。 ページやセクション全体ではなく、インタラクティブなサブコンポーネントのみをラップする
バレルエクスポート
- 各コンポーネントディレクトリに
index.tsを作成してパブリックAPIをエクスポート - ネストしたファイルからではなく、ディレクトリ単位で再エクスポートする
app/ディレクトリにはバレルエクスポートを作成しない(tree-shakingが壊れる)
TypeScriptルール
- strict modeは常に有効。1ファイルだけ無効化も禁止
anyは使わない。本当に型不明な場合はunknownを使い、型ガードで絞り込む- non-null assertion(
!)は使わない。安全な理由をコメントで説明できる場合のみ許容 - オブジェクトの形状には
interfaceを優先。Union、intersection、プリミティブにはtypeを使う - 設定オブジェクトには型を広げずに型チェックできる
satisfiesを使う - ジェネリック型パラメータ: シンプルなケースは単一大文字(
T)、 複雑なケースは説明的な名前(TData、TError)
状態管理
- ローカルUIの状態(開閉、ホバー、フォームフィールドのフォーカス): useState
- フォーム状態: React Hook Form(インストール済み)
- サーバー状態(APIからのデータ): React Query / SWR(インストール済み)
- グローバルクライアント状態(ユーザー設定、認証、カート): Zustand
- 頻繁に変更される状態にContextは使わない(すべてのコンシューマを再レンダリングする)
- 1つのコンポーネントツリーにのみ閉じた状態にZustandは使わない
Zustandストアパターン
// stores/use-auth-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface AuthState {
user: User | null;
setUser: (user: User | null) => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user }),
}),
{ name: 'auth-storage' }
)
);
Tailwind CSSルール
- JSX内でTailwindユーティリティクラスを直接使う。カスタムCSSは絶対に必要な場合のみ
- 条件付きクラスの結合には
lib/utils.tsのcn()(clsx + tailwind-merge)を使う - 任意の値(
w-[317px])はデザイントークンから来る値でない限り使わない - コンポーネントのバリアントには三項演算子ではなく
cva()(class-variance-authority)を使う - レスポンシブプレフィックスの順序: base → sm → md → lg → xl → 2xl
- ダークモード: 別ファイルではなく
dark:プレフィックスクラスを使う
APIルート(App Router)
ルートハンドラはapp/api/[route]/route.tsに配置。パターン:
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
const bodySchema = z.object({
name: z.string().min(1),
});
export async function POST(request: NextRequest) {
const body = await request.json();
const result = bodySchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: result.error.flatten() },
{ status: 400 }
);
}
return NextResponse.json({ success: true });
}
リクエストボディは必ずZodでバリデーション。レスポンスは必ずNextResponse.jsonで型付け。 ルートハンドラからthrowしない——エラーをcatchして適切なステータスコードを返す。
データフェッチ
- Server Componentでは: async/awaitで直接フェッチ(useEffectは不要)
- Client Componentでは: React Query(useQuery、useMutation)を使う
- すべてのfetch呼び出しをtry/catchでラップするか、fetchWithErrorユーティリティを使う
- サーバーサイドの関数メモ化にはNext.jsの
cache()を使う - ミューテーション後はフルページリロードではなく
revalidatePath()/revalidateTag()を使う
next/imageルール
- 画像には必ずnext/imageを使う。
<img>タグは禁止 - fill以外の画像には必ずwidthとheightを指定する
- レスポンシブ画像には
fill+position: relativeのコンテナを使う - altテキストは必ず意味のある内容にする。装飾的な画像のみ
alt=""
テスト(Vitest + Testing Library)
- テストファイルはソースと同じ場所:
ComponentName.test.tsx - 実装ではなく振る舞いをテストする: レンダリング→操作→出力を確認
- 内部状態を直接テストしない
screenクエリの優先順位: ByRole > ByLabelText > ByText > ByTestId- モックはモジュール境界で行う(関数レベルではなく)
- スタイル(クラス名)をテストしない——コンポーネントが正しくレンダリングされ動作するかをテストする
ユニットテストのテンプレート:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserCard } from './UserCard';
const mockUser = { id: '1', name: 'Alice', email: '[email protected]' };
describe('UserCard', () => {
it('ユーザー名を表示する', () => {
render(<UserCard user={mockUser} />);
expect(screen.getByText('Alice')).toBeInTheDocument();
});
it('クリック時にonSelectをuser idで呼び出す', async () => {
const onSelect = vi.fn();
render(<UserCard user={mockUser} onSelect={onSelect} />);
await userEvent.click(screen.getByRole('button', { name: /alice/i }));
expect(onSelect).toHaveBeenCalledWith('1');
});
});
E2Eテスト(Playwright)
- E2Eテストはプロジェクトルートの
e2e/に配置 - 複雑なページにはPage Object Modelを使う
- VitestとPlaywright両方で同じ振る舞いをテストしない——ロジックはユニット、ユーザージャーニーはE2E
data-testidは最小限に。アクセシブルなセレクタ(role、label、テキスト)を優先- E2EはCIのみ実行。ローカルで変更のたびに実行しない
.claudeignore
.next/ node_modules/ .vercel/ out/ dist/ *.generated.ts prisma/migrations/ coverage/ playwright-report/
やってはいけないこと
- コンポーネント内にモックデータを書かない——
__fixtures__/にフィクスチャを置く - クライアントコンポーネントでサーバー専用モジュールをimportしない(実行時エラーになる)
- コンポーネントに
export defaultを使わない - コミットするコードに
console.logを残さない // @ts-ignoreや// @ts-nocheckを使わない- ZustandのStateを直接変更しない——必ずストアのsetterを使う
## Pages Router用 CLAUDE.mdテンプレート
Pages Router(Next.js 12-13またはApp Routerに移行していないレガシーコードベース)を使っている場合、規則がかなり異なるため別のテンプレートが必要になる。主な違いはデータ取得パターン、APIルート、Server Componentsの不在だ。
```markdown
# Project: [アプリ名]
## スタック
- Next.js 13(Pages Router)
- TypeScript 5+(strict mode)
- Tailwind CSS v3
- Jest + Testing Library(ユニット・統合テスト)
- Playwright(E2Eテスト)
- Zustand(グローバル状態管理)
## ディレクトリ構成
src/
pages/ # Next.js Pages Routerページ
api/ # APIルート(pages/api/)
components/
ui/ # プリミティブ
features/ # 機能スコープのコンポーネント
lib/ # ユーティリティ
hooks/ # カスタムフック
stores/ # Zustandストア
types/ # 共有型
## ファイル命名
- Reactコンポーネント: PascalCase(`UserCard.tsx`)
- その他すべて: kebab-case(`use-auth.ts`)
- ページ: URLセグメントに合わせてkebab-case(`user-profile.tsx` → /user-profile)
- APIルート: pages/api/内でkebab-case(`pages/api/user-settings.ts`)
## データフェッチ
- getServerSideProps: リクエストごとに新鮮なデータが必要なページ
- getStaticProps + getStaticPaths: 静的生成ページ
- SWR(インストール済み): クライアントサイドのデータ取得・再検証
- useEffect + fetchパターンは使わない——クライアントデータにはSWRを使う
## APIルート(Pages Router)
APIルートは`pages/api/`に配置。パターン:
```ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { z } from 'zod';
const bodySchema = z.object({ name: z.string() });
type ResponseData = { success: true } | { error: string };
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const result = bodySchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: 'Invalid request body' });
}
res.status(200).json({ success: true });
}
コンポーネント規則・TypeScript・状態管理・Tailwind・テスト
App Routerテンプレートと同じルールを適用。 注意: Pages Routerでは”use client”/“use server”ディレクティブは不要。 すべてのコンポーネントはデフォルトでクライアントコンポーネント。
Pages Routerテンプレートが短いのは、App Routerほど定義すべき判断が多くないためだ。現代のNext.js開発における真の複雑さはApp Routerのサーバー/クライアント境界の扱いにあり、長い方のテンプレートがそこに対処している。
## コンポーネント生成規則の詳細
### ファイル命名
CLAUDE.mdの命名セクションは形式的なものではない。Claude Codeが新しいファイルを作るとき、命名ルールを読んで適用する。ルールがなければ、最近コードベースで見た命名規則をデフォルトとして使う——大規模チームでは一貫性が失われる。
最も重要な規則:
**コンポーネントはPascalCaseファイル。** `UserCard.tsx`(`user-card.tsx`ではなく)。コンポーネント名と一致し、importが予測可能になる。
**コンポーネント以外はkebab-case。** `use-auth.ts`、`date-utils.ts`、`api-client.ts`。フック、ユーティリティ、ストア、定数すべてに適用。
**テストファイルは独立した`__tests__`ディレクトリではなく、ソースと同じ場所に配置。** `UserCard.test.tsx`は`UserCard.tsx`の隣。コロケーションにより、テストが欠けていることが一目でわかり、任意のファイルに対するテストを見つけやすくなる。
**バレルのindexファイルはディレクトリレベル。** `components/ui/`ディレクトリがあれば、すべてを再エクスポートする`components/ui/index.ts`が存在する。コードベース全体のimportは`from '@/components/ui/Button'`ではなく`from '@/components/ui'`を使う。
### "use client"戦略
これはApp Routerプロジェクトで最もインパクトの大きい規則だ。ガイダンスなしでは、Claude Codeは保守的に`"use client"`を追加する傾向がある——`onClick`ハンドラを持つコンポーネントには、より小さな子コンポーネントにそのハンドラを移せる場合でも付与することが多い。
定義すべきルール: **`"use client"`境界はインタラクティビティにできるだけ近い場所(末端)に置く。**
ユーザーデータを表示し、フィルターボタンが1つあるだけのページはClient Componentにすべきではない。そのページはServer Componentとしてデータを直接レンダリングする。フィルターボタン——そこだけ——を`"use client"`ディレクティブを持つ小さなClient Componentとして切り出す。
このパターンがもたらすもの:
1. データフェッチをサーバーに保持(高速、ローディング状態不要)
2. クライアントサイドのJavaScriptバンドルを削減
3. 各コンポーネントのパフォーマンス特性がファイルを見れば明確
これをCLAUDE.mdに明示的に書いておけば、Claude Codeはインタラクティビティに触れるものすべてに`"use client"`をデフォルトで付けるのではなく、この設計に沿った新しいコンポーネントを構成するようになる。
### Props Interfaceパターン
Props interfaceパターンを定義しておくと、スタイルの不一致を大量に防げる:
```typescript
// CLAUDE.mdが生成すべき形:
interface UserCardProps {
user: User;
variant?: 'compact' | 'full';
onSelect?: (id: string) => void;
}
export function UserCard({ user, variant = 'full', onSelect }: UserCardProps) {
// ...
}
ここでの規則:ComponentNamePropsという名前のinterfaceを、コンポーネント関数の直前、同じファイルに定義する。型が複数のコンポーネントで共有される場合を除き、別の型ファイルには置かない。
テスト設定と考え方
Vitest設定
VitestはほとんどのNext.jsプロジェクトでJestより高速で、JestのAPIを共有しているため移行は通常スムーズだ。Next.js向けの主要な設定:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
},
});
// src/test/setup.ts
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
afterEach(() => {
cleanup();
});
この設定をCLAUDE.mdのテストセクションに含めておくと、Claude Codeがjest.fn()の代わりにvi.fn()を生成し、jestではなくvitestのimportを使うようになる。
何をテストするか
ユニットテストの対象と対象外をClaude Codeに明示しておく。このガイダンスがないと、すべてに対してテストを生成しようとする傾向があり、些細なレンダリングチェック(スタイル変更で壊れる)が大量に生まれる。
定義すべきガイダンス:
テストを書く対象:
- propsや状態に基づく条件付きレンダリングを持つコンポーネント
- ユーザーインタラクション(フォーム、ボタン、キーボードナビゲーション)を持つコンポーネント
- ロジックを持つカスタムフック
- エッジケースを持つユーティリティ関数
- エラー状態とローディング状態
テストを書かない対象:
- ロジックなしでpropsを直接レンダリングする静的表示コンポーネント
- 単純なラッパーコンポーネント
- ページ(E2Eテストでカバー)
- CSSクラス名とスタイリング
Playwrightでユーザージャーニーをテストする
PlaywrightテストはE2Eテストとしてe2e/に配置し、実装の詳細ではなくユーザージャーニーを表すべきだ。Page Object Modelパターンにより、UIが変化してもテストが読みやすい状態を保てる:
// e2e/pages/dashboard.page.ts
import { type Page, type Locator } from '@playwright/test';
export class DashboardPage {
readonly heading: Locator;
readonly createButton: Locator;
constructor(private page: Page) {
this.heading = page.getByRole('heading', { name: 'Dashboard' });
this.createButton = page.getByRole('button', { name: 'Create new' });
}
async goto() {
await this.page.goto('/dashboard');
}
async createItem(name: string) {
await this.createButton.click();
await this.page.getByLabel('Item name').fill(name);
await this.page.getByRole('button', { name: 'Save' }).click();
}
}
このパターンをCLAUDE.mdに含めておけば、Claude Codeが新しいE2Eテストを生成するとき、インラインのロケーター記述ではなくPage Objectを作成するようになる。
チーム向けAGENTS.md
複数の開発者がClaude Codeを同じプロジェクトで使う場合、AGENTS.mdファイルによって特定のディレクトリに適用するコンテキストを追加できる。プロジェクトルートのCLAUDE.mdと異なり、AGENTS.mdはサブツリーにスコープできる。
共有コンポーネントライブラリ向けのcomponents/AGENTS.md:
# コンポーネントライブラリ エージェントガイドライン
## コンポーネント作成チェックリスト
このディレクトリに新しいコンポーネントを作成するとき:
1. ディレクトリのindex.tsバレルエクスポートにコンポーネントを追加する
2. コロケーション配置のテストファイル(ComponentName.test.tsx)を作成する
3. コンポーネントに複数のバリアントがある場合はStorybookのストーリーを追加する
4. すべてのpropsにJSDocコメントを記述する
## アクセシビリティ要件
すべてのインタラクティブコンポーネントは以下を満たすこと:
- 正しいARIAロールを持つ
- キーボードナビゲーションをサポートする(Tab、Enter、Space、Escape)
- スクリーンリーダーで正しく動作する(テストでaxe-coreを使用)
- 視覚的なフォーカスインジケーターを持つ(:focus-visibleスタイルを削除しない)
## 追加してはいけないもの
- UIコンポーネントにビジネスロジック
- UIコンポーネントにデータフェッチ
- ui/コンポーネントへのZustandストアの直接アクセス(propsでデータを渡す)
## バリアント命名
バリアントには`cva()`を使う。バリアント名はデザインシステムに合わせる:
- size: 'sm' | 'md' | 'lg'
- variant: 'primary' | 'secondary' | 'ghost' | 'destructive'
APIレイヤー向けのapp/api/AGENTS.md:
# APIルート エージェントガイドライン
## ルートハンドラのパターン
すべてのルートハンドラは以下を満たすこと:
1. リクエストメソッドを明示的にバリデーション
2. リクエストボディ/パラメータをZodでバリデーション
3. 一貫したエラー形式を返す: { error: string } | { errors: ZodError }
4. エラーレスポンスを返す前にサーバーサイドでログを記録
5. 内部エラーメッセージやスタックトレースをレスポンスに含めない
## 認証
- auth helperは@/server/authからimport(@/libからは不可)
- データアクセスの前に認証を確認する
- 未認証には401、権限なしには403を返す(どちらも401にしない)
## データベースアクセス
- DBの呼び出しは@/server/repositoriesのリポジトリレイヤーを経由する
- ルートハンドラからPrismaを直接呼び出さない
- すべてのDB操作をtry/catchでラップする
React向け .claudeignore
.claudeignoreファイルは、Claude Codeがコンテキストから除外するファイルとディレクトリを指定する。Next.jsプロジェクトで除外すべき重要な項目:
# ビルド成果物——コード生成に関係ない
.next/
out/
dist/
build/
# 依存関係——大きくてアプリケーションロジックに無関係
node_modules/
# デプロイ成果物
.vercel/
.netlify/
# 生成ファイル——編集しても意味がない
*.generated.ts
*.generated.tsx
prisma/migrations/
graphql/generated/
# テストカバレッジレポート
coverage/
playwright-report/
test-results/
# 環境変数ファイル——コードでもコンテキストでもない
.env
.env.local
.env.production
node_modules/の除外が最もインパクトが大きい。これがないと、Claude CodeはライブラリのソースコードをスキャンしてAPIを理解しようとすることがあり、コンテキストウィンドウを無駄に使い、パッケージのTypeScript宣言を読むより劣った結果を生む。
実践的なワークフロー:コンポーネント駆動開発
CLAUDE.mdを配置したNext.js App RouterプロジェクトでのClaude Codeの実際のワークフローを見てみよう。
ドメイン型から始める。 コンポーネントを生成する前に、データのTypeScript interfaceを定義するようClaude Codeに依頼する。プロンプト例:「src/types/project.tsにProjectインターフェースを追加して。プロジェクトはid、name、description、status(active/archived/draft)、owner(Userの参照)、createdAtを持つ。」
これをレビューして先にコミットする。以降に構築するものはすべてこの型から派生する。
サーバーデータレイヤーを生成する。 型ができたら:「src/server/repositories/projects.tsに、現在のユーザーのすべてのプロジェクトをDBからフェッチするgetProjects関数を作成して。types/からProjectの型を使うこと。」
Server Componentページを生成する。 「app/(dashboard)/projects/page.tsxにプロジェクト一覧ページを作成して。サーバーレイヤーからgetProjectsを呼び出してProjectListコンポーネント(まだ存在しない——projects: Project[]を受け取るスタブを作成)をレンダリングするServer Componentにすること。」
クライアントインタラクションを追加する。 「ProjectListコンポーネントにユーザーがstatusでフィルタリングできるフィルターバーが必要。フィルターバーだけをClient Component(‘use client’付きのProjectFilter.tsx)として切り出して。リスト本体は今はServer Componentのスタブのままでいい。」
このシーケンスはApp Routerのアーキテクチャを尊重し、クライアント境界を狭く保ち、CLAUDE.mdの命名・規則ルールに準拠したファイルを生成する。各ステップで大量のコードダンプではなく小さくレビューしやすいdiffが生まれる。
CLAUDE.mdで対処すべきよくある落とし穴
Reactコードベース全般で見られるパターンから、明示しないと問題になりやすい箇所を挙げる:
export default対named export。 指示がなければClaude Codeはコンポーネントにexport defaultを使う。Named exportはほぼ例外なくReactコンポーネントに向いている——auto-importが効き、リファクタリングツールがより確実に動き、バレルエクスポートが一貫する。これを明示的に指定する。
Error boundaryの配置。 ガイダンスなしでは、Claude CodeはError boundaryをまったく追加しない。どこに置くべきかを指定する:通常はルートセグメント、データフェッチコンポーネント、サードパーティ埋め込みの周囲。
ローディング状態。 App RouterにはSuspenseベースのローディングUIのためのloading.tsxファイルがある。CLAUDE.mdにガイダンスがないと、Claude Codeはルートレベルのloading.tsxではなくClient Component内でuseStateによる手動ローディング状態管理を生成することがある。ローディングUIはルートレベルのloading.tsxに置くと指定する。
環境変数のバリデーション。 lib/env.tsのZodスキーマで起動時に環境変数をバリデーションすること、アプリケーションコードにprocess.env.Xを生で書かないことを指定する。
ルールコレクションとの連携
このガイドのパターンは、Claude Code設定のより大きなエコシステムの一部だ。複数のプロジェクトにまたがってルールを管理したり、チームで設定を共有したりする場合は、ルールコレクションにフレームワーク・テストライブラリ・ワークフローパターン別に整理された再利用可能なCLAUDE.mdスニペットがある。
Reactプロジェクトでは、react、nextjs、typescript-strictタグが付いたエントリを確認してほしい——国際化(next-intl)、認証(next-auth / Clerk)、CMS連携(Contentful、Sanity)に関するさらなるエッジケースが含まれており、このガイドの範囲を補完している。
本番のCLAUDE.mdはどんな形か
上記のテンプレートは出発点であって最終形ではない。成熟したNext.jsプロジェクトの本番CLAUDE.mdは通常もっと長い——300〜600行程度——なぜなら数ヶ月かけてチームが行った判断が蓄積されているからだ:どのRadix UIプリミティブがラップ・カスタマイズされているか(新規作成禁止)、どのZodスキーマが共有されているか(lib/schemas/からimport、再定義禁止)、どのServer Actionが利用可能か(新しいfetch呼び出しを作らずexistingのアクションを使う)、フィーチャーフラグはどのフック(lib/flagsのuseFeatureFlagフック)でチェックするか。
CLAUDE.mdを構築する正しい方法はインクリメンタルだ。上記のテンプレートから始める。Claude Codeがチームの規則に合わないものを生成したとき、ルールを追加する。3ヶ月積極的に開発すれば、CLAUDE.mdはリポジトリで最も価値あるファイルの1つになる——プロジェクトの動作方法のリビングスペックとして、人間にとってもAIエージェントにとってもオンボーディングに役立つ。
別の選択肢は、AIの出力をスタイル違反で永遠にレビューし続けることだ。規則をCLAUDE.mdに定義することで、レビューはスタイル修正からロジックのレビューに変わる——それこそが本当に重要な作業だ。
FAQ
Claude CodeはApp RouterとPages Routerの両方で使える?
使える。重要なのは、どちらのルーターを使っているかをCLAUDE.mdに明記することだ。命名・TypeScript・Tailwind・テストなど大半の規約は共通で、データ取得とAPIルートのパターンだけが異なる。そこはルーター固有のテンプレートセクションで対応する。
CLAUDE.mdはどこに置くべき?
package.jsonと同じプロジェクトルートに置き、バージョン管理にコミットする。Claude Codeはセッション開始時に自動で読み込むため、すべての貢献者とAI生成コードが同じ規約を共有できる。
Claude Codeが全コンポーネントに"use client"を付けるのを止めるには?
CLAUDE.mdにRSCファーストのルールを書く。コンポーネントはデフォルトでServer Component、"use client"はuseState・useEffect・イベントハンドラ・ブラウザAPI・クライアント専用フックを使う場合のみ。さらに境界をページ全体ではなく最小のインタラクティブなサブコンポーネントに押し下げるよう指示する。
ReactチームはCLAUDE.mdとAGENTS.mdのどちらを使うべき?
CLAUDE.mdはClaude Code専用、AGENTS.mdは28以上のAIコーディングツールが対応するクロスツール標準。全員がClaude Codeを使うならCLAUDE.mdで統一、複数ツール併用チームは共通規約をAGENTS.mdに置いてCLAUDE.mdから参照する構成が適している。
本番のCLAUDE.mdはどれくらいの長さが適切?
成熟したNext.jsプロジェクトでは300〜600行程度に落ち着くのが一般的。テンプレートから始めて、Claude Codeが規約に合わないコードを生成するたびにルールを追加していく。
CLAUDE.mdで強制すべきTypeScriptルールは?
効果が大きいのは5つ:strictモード常時ON、any禁止(unknown+型ガード)、理由を説明できない非nullアサーション禁止、オブジェクト形状はinterface・ユニオンはtype、設定オブジェクトにはsatisfies。AI生成TypeScriptが最もブレやすい領域だ。
関連記事
- Claude Code × Python プロジェクト: CLAUDE.md テンプレートと AGENTS.md パターン
- AGENTS.md vs CLAUDE.md どっちを使う?5つの違いと正解(2026年版)
- ユースケース別 AGENTS.md・CLAUDE.md・.cursorrules テンプレート集
- Claude Code Hooks 完全リファレンス 2026
デプロイ先の選択:Claude Codeプロジェクトの公開
CLAUDE.mdが整ったNext.jsプロジェクトができたら、次はデプロイ先の選択だ。主要な選択肢は2つある。
Netlify はGitHubと連携してpushごとに自動デプロイする。無料枠で個人開発・サイドプロジェクトの大半のトラフィックをカバーでき、デプロイプレビュー・ブランチデプロイ・エッジ関数がNext.js App Routerとクリーンに統合される。トラフィック増でVercelの料金が厳しくなるプロジェクトでは、Netlifyが標準的な代替になる。
Vercel はファーストパーティサポートがある分Next.jsのデフォルトだが、スケール時の料金モデルには注意が必要だ。長期運用するプロジェクトでは、Netlifyの予測しやすい料金と素直なCI/CDが勝ることが多い。
静的エクスポート(next export)ならどちらでも同等に動く。Server Components・Server Actionsを使うフルApp Router構成なら、早めにビルドパイプラインを検証しておこう——netlify.tomlの設定は最小限で済むが、公開前の確認には値する。