9.3 KiB
| applyTo |
|---|
| ** |
Next.js LLMベストプラクティス(2025年)
最終更新:2025年7月
このドキュメントは、Next.jsアプリケーションの構築、構造化、保守における最新の権威あるベストプラクティスをまとめています。LLMと開発者がコード品質、保守性、スケーラビリティを確保するために使用することを意図しています。
1. プロジェクト構造と組織
-
app/ディレクトリを使用(App Router)すべての新規プロジェクトで。従来のpages/ディレクトリより優先。 -
トップレベルフォルダ:
app/— ルーティング、レイアウト、ページ、ルートハンドラーpublic/— 静的アセット(画像、フォントなど)lib/— 共有ユーティリティ、APIクライアント、ロジックcomponents/— 再利用可能なUIコンポーネントcontexts/— Reactコンテキストプロバイダーstyles/— グローバルとモジュラースタイルシートhooks/— カスタムReactフックtypes/— TypeScript型定義
-
コロケーション: ファイル(コンポーネント、スタイル、テスト)は使用される場所の近くに配置するが、深いネスト構造は避ける。
-
ルートグループ: 括弧を使用(例:
(admin))してURLパスに影響を与えずにルートをグループ化。 -
プライベートフォルダ:
_で接頭辞(例:_internal)を付けてルーティングをオプトアウトし、実装詳細を示す。 -
機能フォルダ: 大規模なアプリの場合、機能でグループ化(例:
app/dashboard/、app/auth/)。 -
src/を使用(オプション): 設定ファイルから分離するためにすべてのソースコードをsrc/に配置。
2.1. サーバーとクライアントコンポーネントの統合(App Router)
サーバーコンポーネント内で{ ssr: false }を使ったnext/dynamicを絶対に使用しない。 これはサポートされておらず、ビルド/ランタイムエラーを引き起こします。
正しいアプローチ:
- サーバーコンポーネント内でクライアントコンポーネント(例:フック、ブラウザAPI、またはクライアント専用ライブラリを使用するコンポーネント)を使用する必要がある場合:
- すべてのクライアント専用ロジック/UIを専用のクライアントコンポーネントに移動する(上部に
'use client'を付ける)。 - サーバーコンポーネントでそのクライアントコンポーネントを直接インポートして使用する(
next/dynamicは不要)。 - 複数のクライアント専用要素(例:プロフィールドロップダウン付きのナビゲーションバー)を組み合わせる必要がある場合、それらすべてを含む単一のクライアントコンポーネントを作成する。
- すべてのクライアント専用ロジック/UIを専用のクライアントコンポーネントに移動する(上部に
例:
// サーバーコンポーネント
import DashboardNavbar from '@/components/DashboardNavbar';
export default async function DashboardPage() {
// ...サーバーロジック...
return (
<>
<DashboardNavbar /> {/* これはクライアントコンポーネント */}
{/* ...サーバーレンダリングページの残り... */}
</>
);
}
理由:
- サーバーコンポーネントはクライアント専用機能やSSRが無効化された動的インポートを使用できない。
- クライアントコンポーネントはサーバーコンポーネント内でレンダリングできるが、その逆はできない。
まとめ:
常にクライアント専用UIをクライアントコンポーネントに移動し、サーバーコンポーネントで直接インポートしてください。サーバーコンポーネントで{ ssr: false }を使ったnext/dynamicを絶対に使用しない。
2. コンポーネントベストプラクティス
- コンポーネントタイプ:
- サーバーコンポーネント(デフォルト): データフェッチ、重いロジック、非インタラクティブUIのため。
- クライアントコンポーネント: 上部に
'use client'を追加。インタラクティブ性、状態、ブラウザAPIのために使用。
- コンポーネントを作成するタイミング:
- UIパターンが複数回再利用される場合。
- ページのセクションが複雑または自己完結型の場合。
- 可読性やテスト可能性を向上させる場合。
- 命名規則:
- コンポーネントファイルとエクスポートには
PascalCaseを使用(例:UserCard.tsx)。 - フックには
camelCaseを使用(例:useUser.ts)。 - 静的アセットには
snake_caseまたはkebab-caseを使用(例:logo_dark.svg)。 - コンテキストプロバイダーを
XyzProviderとして命名(例:ThemeProvider)。
- コンポーネントファイルとエクスポートには
- ファイル命名:
- コンポーネント名をファイル名と一致させる。
- 単一エクスポートファイルの場合、コンポーネントをデフォルトエクスポート。
- 関連する複数コンポーネントの場合、
index.tsバレルファイルを使用。
- コンポーネント配置:
- 共有コンポーネントを
components/に配置。 - ルート固有コンポーネントを関連するルートフォルダー内に配置。
- 共有コンポーネントを
- Props:
- PropsにはTypeScriptインターフェースを使用。
- 明示的なprop型とデフォルト値を優先。
- テスト:
- テストをコンポーネントとコロケート(例:
UserCard.test.tsx)。
- テストをコンポーネントとコロケート(例:
3. 命名規則(全般)
- フォルダ:
kebab-case(例:user-profile/) - ファイル: コンポーネントには
PascalCase、ユーティリティ/フックにはcamelCase、静的アセットにはkebab-case - 変数/関数:
camelCase - 型/インターフェース:
PascalCase - 定数:
UPPER_SNAKE_CASE
4. APIルート(ルートハンドラー)
- 超低遅延や地理的分散が必要でない限り、Edge FunctionsよりAPIルートを優先。
- 配置: APIルートを
app/api/に配置(例:app/api/users/route.ts)。 - HTTPメソッド: HTTP動詞に名前を付けた非同期関数をエクスポート(
GET、POSTなど)。 - リクエスト/レスポンス: Web
RequestとResponseAPIを使用。高度な機能にはNextRequest/NextResponseを使用。 - 動的セグメント: 動的APIルートには
[param]を使用(例:app/api/users/[id]/route.ts)。 - バリデーション: 常に入力を検証・無害化。
zodやyupのようなライブラリを使用。 - エラーハンドリング: 適切なHTTPステータスコードとエラーメッセージを返す。
- 認証: ミドルウェアまたはサーバーサイドセッションチェックを使用して機密ルートを保護。
5. 全般的ベストプラクティス
- TypeScript: すべてのコードでTypeScriptを使用。
tsconfig.jsonでstrictモードを有効化。 - ESLint & Prettier: コードスタイルとリンティングを強制。公式Next.js ESLint設定を使用。
- 環境変数: シークレットを
.env.localに保存。シークレットをバージョン管理にコミットしない。 - テスト: Jest、React Testing Library、またはPlaywrightを使用。すべての重要なロジックとコンポーネントのテストを書く。
- アクセシビリティ: セマンティックHTMLとARIA属性を使用。スクリーンリーダーでテスト。
- パフォーマンス:
- 組み込み画像とフォント最適化を使用。
- 非同期データにはSuspenseとローディング状態を使用。
- 大きなクライアントバンドルを避ける;ほとんどのロジックをサーバーコンポーネントに保つ。
- セキュリティ:
- すべてのユーザー入力を無害化。
- 本番環境でHTTPSを使用。
- セキュアなHTTPヘッダーを設定。
- ドキュメント:
- 明確なREADMEとコードコメントを書く。
- パブリックAPIとコンポーネントを文書化。
不要なサンプルファイルを避ける
ユーザーが具体的にライブサンプル、Storybookストーリー、または明示的なドキュメントコンポーネントを要求しない限り、メインコードベースにサンプル/デモファイル(ModalExample.tsxなど)を作成しないでください。デフォルトでリポジトリをクリーンで本番重視に保ってください。
常に最新のドキュメントとガイドを使用
- すべてのnextjs関連のリクエストについて、最新のnextjsドキュメント、ガイド、例を検索することから始めてください。
- 利用可能な場合は以下のツールを使用してドキュメントを取得・検索してください:
resolve_library_idでドキュメント内のパッケージ/ライブラリ名を解決。get_library_docsで最新のドキュメントを取得。