Next.js Webアプリケーション コーディング規約
要求された作業にはこれらの規約を適用します。明示的なユーザー指示は、このスキルのすべてのルールやデフォルト設定より優先されます。既存のプロジェクト要件と統合を維持します。このスキルを適用しても、関連のない移行やオプションの依存関係すべてのインストールは許可されません。
実装前に、プロジェクトの指示、インストールされているバージョン、および既存の規約を検査してください。利用可能な場合は、関連するインストール済みのNext.jsドキュメント(特に node_modules/next/dist/docs/)と、そうでない場合はバージョンに適した公式ドキュメントを参照してください。別のバージョンのフレームワークAPIを想定しないでください。
デフォルトと条件付き機能
| 領域 | デフォルト | 上書きまたはアクティベーションルール |
|---|---|---|
| フレームワーク | Next.js App Router と厳格なTypeScript | プロジェクトが選択したバージョンと明示的なユーザーの選択を尊重します。 |
| コンポーネント | Base UI (Tailwind CSS付き、ローカルコンポーネントでラップ) | Radix、shadcn/ui、またはその他の要求されたライブラリをサポートします。同じコンポーネント編成を維持します。 |
| バリアント | class-variance-authority と Tailwind 対応のクラスマージヘルパー | 確立された同等のものを再利用します。 |
| APIトランスポート | カスタム APIClient および ServerAPIClient 抽象化の背後にあるAxios | 既存のカスタムクライアント、fetch、またはその他の要求されたトランスポートも有効です。コントラクトはトランスポートから独立している必要があります。 |
| バリデーション | Zodスキーマ (推論されたTypeScript型付き) | 外部データ境界ではデフォルトで必須。明示的なユーザー指示がある場合のみ変更します。 |
| リモートクライアントデータ | TanStack Query (共有クエリラッパー付き) | リクエストされた同等のものを再利用します。 |
| 共有インタラクティブ状態 | Zustand | 単一コンポーネントが所有する状態にはローカルReact状態を使用します。明示的な代替案を尊重します。 |
| 認証 | 認証が要求された場合のプロバイダーニュートラルな電子メールOTPフロー | 選択されたプロバイダーとリクエストされたログイン方法に従います。プロバイダーを暗黙的に選択またはプロビジョニングしないでください。 |
| クロスタブ同期 | 関連する共有ミューテーションイベントに対して有効 | オプションであり、リクエストに応じて削除可能。クロスタブの一貫性が役立つ場合にのみイベントを追加します。 |
| ロギングとトレース | 既存のロギングシステムに従う | 存在しない場合、要求されない限り、ロギング/トレースインフラストラクチャを導入、提案、または言及しないでください。 |
| モックデータとシミュレーション | オフ | ユーザーがモックデータまたは開発/テストシミュレーションを要求した場合にのみ追加します。 |
| 開発ツール | 個別の開発専用モジュール | 関連するか要求された場合にのみツールを追加します。規定されたツールやオーバーレイはありません。 |
決定が必要な場合はデフォルトを選択してください。各デフォルトについて質問しないでください。認証、状態ストア、同期、またはインフラストラクチャを、それらを必要としない機能に導入しないでください。
必須フォルダ構造
新しいコードにはこの編成を使用してください。プロジェクトに空のプレースホルダーを埋めるのではなく、責任が生じたときにディレクトリを作成します。既存のプロジェクトでは、移行リクエストなしに関連のないファイルを移動しないでください。
src/
├── app/ ルート、レイアウト、APIエンドポイント
├── components/
│ ├── ui/ 共有UIプリミティブ
│ ├── standalone/ 再利用可能なドメインコンポーネント
│ ├── pages/<page>/ ページ固有のコンポーネント
│ └── <feature>/ 機能サブシステム
├── controllers/
│ ├── client/<domain>/ ブラウザAPI呼び出し
│ └── server/<domain>/ サーバーサイドAPI呼び出し
├── providers/ Reactコンテキストプロバイダー
├── stores/ Zustandストア
├── hooks/ 共有Reactフック
├── helpers/ 認証/アクセスおよびその他のワークフロー
├── lib/ APIクライアント、キャッシュ、ロギング、同期
├── db/ データベース/Redisクライアント
├── utils/ 小さなユーティリティと定数
└── assets/icons/ ソースSVG
インフラストラクチャのフォルダ名は配置を説明し、必要なサービスではありません。このツリーによって、データベース、Redis、キャッシュ、またはロギングスタックが暗黙的に示されることはありません。
- これらの責任フォルダの下にドメインコードを整理します。これらを別のトップレベルのアーキテクチャに置き換えないでください。
- PascalCaseのコンポーネントファイル、
useSomething.tsフック (JSXが存在する場合のみ.tsx)、およびupdateName.tsのようなキャメルケースの操作ファイルを使用してください。 - 説明的なドメインと機能ディレクトリを使用します。
page.tsx、layout.tsx、route.tsなどのフレームワークファイル名は変更しないでください。 index.tsを使用してコントローラーの名前空間を構成し、index.tsxをマルチファイルコンポーネントのエントリポイントとして使用します。srcにルートがあるインポートには@/を使用します。近い内部の兄弟には相対インポートを使用します。型のみの依存関係にはimport typeを使用します。- 一時的なコピー、バックアップフォルダー、重複する実装、および責任が不明確なキャッチオールファイルは避けてください。
ルートとコンポーネント構成
app/ はルーティング、レイアウト構成、リクエスト処理、および初期データ読み込みに集中させてください。レイアウト境界が役立つ場合は、(auth)、(no-auth)、(settings) などのルートグループを使用します。ルートグループ名だけではアクセス保護は提供されません。
ページは薄く保ちます。初期データを読み込み、ページレベルの決定を解決し、components/pages/<page>/ または関連する機能ディレクトリからコンポーネントをレンダリングします。サーバーコンポーネントがデフォルトです。ブラウザの状態、イベント、およびAPIのためにクライアント境界を導入します。
コンポーネントの配置:
components/ui/: ドメインに依存しないプリミティブと、選択されたUIライブラリのラッパー。components/standalone/: ページや機能間で再利用されるドメインコントロール。components/pages/<page>/: 1つのページに固有のコンポーネント。components/<feature>/: ナビゲーションやコマンドインターフェースなど、ルート間で共有される大規模なサブシステム。
複雑なコンポーネントを焦点を絞ったファイルに分割します。 これは中核的な規約です。重要なコンポーネントは、index.tsx 構成エントリポイントと、必要に応じて名前付きのサポートコンポーネント、タブ、行、フック、および型を持つディレクトリを持つ必要があります。1つのファイルに複数の重要なサブコンポーネントと無関係なエフェクトを蓄積しないでください。任意の行制限ではなく、責任によって分割します。些細なマークアップの断片化は避けてください。
インタラクションの仕組み(キーボードナビゲーション、選択、イベントサブスクリプション、ドラッグ動作、ビュー調整)は、機能ローカルのフックまたは焦点を絞ったハンドラーコンポーネントに保持します。フックが共有される場合は、src/hooks/ に移動します。入力ショートカットは、編集可能なコントロールに干渉しないようにスコープを設定し、アンマウント時にリスナーをクリーンアップします。
プリミティブライブラリをローカルでラップし、Dialog、DialogTrigger、DialogContent、DialogTitle などの構成可能なパーツを公開します。アクセシブルなセマンティクス、フォーカス動作、および実際のライブラリの構成APIを維持します。互換性を確認せずに、Radix固有のプロパティをBase UIラッパーにコピーしないでください。
ラッパーのプロパティは、基になるプリミティブまたはネイティブ要素から型指定し、必要なカスタムプロパティのみを追加します。サイズ、強調、および状態に型付きバリアントを使用します。コンシューマークラスを予測可能にマージし、インストールされているReact/ライブラリAPIに従ってrefサポートを維持します。
Tailwindユーティリティに接続されたセマンティックCSS変数を使用します。テーマ値を集中管理し、コンポーネントバリアントをローカルに保ちます。このスキルは、視覚的なテーマ、間隔スケール、または製品デザインではなく、スタイル編成を規定します。
関連するコンテキストプロバイダーを app/providers.tsx で構成します。それらの実装は providers/ に保持します。ブラウザ専用のプロバイダーは明示的なクライアント境界の背後に置き、開発ツールの初期化を通常のプロバイダーの責任と混在させないでください。
コントローラーとHTTPクライアント
controllers/client/<domain>/ または controllers/server/<domain>/ の下で、ファイルごとに1つの操作を使用します。index.ts ファイルを通じてネストされた名前空間オブジェクトを構成します。例:
ClientController.org.members.list()
ClientController.profile.updateName(input)
ServerController.org.info()
コントローラーは、型付きの操作とエンドポイント構築を提供します。コンポーネントは、URLやトランスポート構成を組み立てるのではなく、コントローラーを呼び出す必要があります。コントローラーは薄く保ちます。再利用可能なワークフローロジックは helpers/ に、インフラストラクチャは lib/ に配置します。
ブラウザとサーバーのHTTPクライアントは分離してください:
lib/APIClient.ts: ブラウザセーフなトランスポート、クエリ/ボディシリアライゼーション、応答処理、およびキャンセルサポート。lib/ServerAPIClient.ts: サーバー専用トランスポート、信頼できるベースURL、およびリクエストスコープの認証情報/セッションコンテキスト。- サーバーシークレットとサーバー専用の依存関係をブラウザのインポートに含めないでください。共有のミュータブルなシングルトンにユーザーごとの認証情報を保持しないでください。
- サーバーコントローラーがアプリケーションエンドポイントを呼び出す場合、必要な認証情報のみを信頼できる宛先に転送します。呼び出し元から提供されたIDヘッダーを認証として信頼しないでください。
- トランスポートエラー、非2xx応答、およびアプリケーションエラーを一貫して正規化します。意味のあるステータス/コード情報を保持します。失敗したリクエストを成功した空のデータに変換しないでください。
- コントローラーとトランスポート層を通じて
AbortSignalをサポートします。キャンセルはエラー通知やリトライをトリガーしてはいけません。
APIスキーマ、コントラクト、ハンドラー
Zodスキーマは、入力のデフォルトの信頼できる情報源です。ボディ、クエリ、パスパラメーターに対して、フォーマット、許容値、長さ/範囲の制限、オプション性、および意図的な型強制を含む、特定のスキーマを定義します。外部値を使用する前に検証します。コントラクトの代わりとして、z.any()、幅広いレコード、キャスト、および非nullアサーションを避けてください。各境界に対して意図的に不明なキーを処理します。
デフォルトでは、メソッド固有のスキーマとエクスポートされたリクエスト/クエリ/レスポンスの型をルートにコロケーションします。リクエスト型は z.infer で推論します。明示的なレスポンスコントラクトを定義し、ランタイムバリデーションが必要な場合はレスポンススキーマを使用します。これらの型をコントローラーとコンポーネントで再宣言する代わりに再利用します。ランタイムコントラクトをブラウザコードと共有する必要がある場合、またはルートモジュールのエクスポート制約で必要な場合は、隣接する schema.ts または types.ts と型のみのインポートを使用して、サーバー実装がクライアントバンドルにプルされないようにします。
一貫性のある判別されたレスポンスエンベロープを使用します:
type APIResponse<T> =
| { success: true; data: T; error: null }
| {
success: false;
data: null;
error: { code: string; message: string; details?: unknown };
};
details を使用する場合は、明示的な安全なバリデーション/エラー詳細型に絞り込みます。すべてのブランチでエラーとHTTPステータスを一貫させます。実稼働応答で内部例外やスタックトレースを公開しないでください。
マルチメソッドルートを、get_request や post_request などのメソッド固有のスキーマ/型セクションと関数に整理します。lib/api/APIRequestHandler.ts の withAPIRequestHandler() を通じてハンドラーをエクスポートします。
lib/api/error/ を維持し、APIError クラスと、エラーコード、メッセージ、ステータスのドメインごとに整理された型付きカタログを用意します。共有ハンドラーは、バリデーションエラー、ドメインエラー、および予期しないエラーを同じレスポンスコントラクトにマッピングします。上記のロギング条件が満たされた場合にのみロギングを統合します。
繰り返し発生する組織ルックアップ、ID解決、クエリ解析、およびアクセスワークフローを helpers/ に抽出します。小さな純粋変換と定数は utils/ に保持します。大規模な統合は焦点を絞ったワークフローファイルに分割します。ルートはこれらのステップを調整し、統合全体を含むべきではありません。
データ取得と状態
適切な場合はサーバーコントローラーを介して初期ページデータをロードし、型付きでシリアル化可能なプロパティをインタラクティブコンポーネントに渡します。独立したリクエストには Promise.all を使用します。依存関係のある操作には順序を維持します。
リモートクライアントデータにはTanStack Queryを使用します。共有デフォルトと、APIエンベロープをアンラップし、エラーを伝播し、クエリのキャンセルシグナルを転送する useApiQuery ラッパーを提供します。クエリキーには、関連するID/リソース/フィルター入力を含める必要があります。サーバーサイドのクエリクライアントはリクエストごとにスコープします。ミューテーション後に影響を受けるクエリエントリを無効化または更新します。
選択、フィルター、パネル、コマンド状態などの共有インタラクティブ状態には、機能固有のZustandストアを使用します。個々のスライスにサブスクライブするか、グループ化された値には浅い選択を使用します。セッター/アクションを明示的にし、所有リソースが変更されたときに状態をリセットします。Queryがすでに所有しているリモートデータの2番目の独立したコピーをZustandに維持しないでください。
コンポーネント所有の入力、ダーティ状態、バリデーションメッセージ、および保存状態には、ローカル状態を使用します。重複送信を防止し、失敗時にユーザー入力を保持します。必須のフォームライブラリはありません。
古くなった検索/フィルターリクエストをキャンセルし、クリーンアップ時にアボートします。新しいリソースを更新する古い完了に対してガードします。実際のキャンセルのみをキャンセルとして分類します。ネットワーク障害を黙って飲み込まないでください。
大規模なリストには、ロード、空、エラー、再試行、およびリスト終了状態とともに、役立つ場合に仮想化とページネーションを適用します。リソース/フィルターが変更されたときに、ページネーションと選択を適切にリセットします。キーボードナビゲーションを仮想化された行と一貫させます。
オプションのクロスタブ同期
ユーザーが無効にしない限り、関連する共有ミューテーションの同期をデフォルトで有効にします。同期の必要性なしに、分離された状態に追加しないでください。
lib/local-sync/ の型付きイベントレジストリ、events/ の下のドメインイベントスキーマ、および共有 hooks/useLocalSync.ts フックを使用します。ペイロード型をZodから推論し、送信メッセージと着信メッセージの両方を検証します。
ストレージイベントの実装は、他のタブと同一タブのリスナーに通知し、永続的なアプリケーション状態ではなく一時的なイベントペイロードを使用し、リスナーをクリーンアップする必要があります。既存の同等のトランスポートも許容されます。エコーループと重複処理を防止します。シークレットを同期しないでください。イベントは、正規の状態所有者を更新または無効化する必要があります。クロスタブイベントはUI調整であり、認証や配信保証ではありません。
認証と認可
このセクションは、認証が範囲内にある場合に適用します。実装はプロバイダーニュートラルに保ち、選択されたサービスまたはカスタムバックエンドに適応します。
- 電子メールOTP認証の場合、コード送信、コード検証、およびサインアウトハンドラーを分離します...
... [翻訳のために切り詰められました]