React フロントエンド
すべての React フロントエンドのためのルール。このファイルには常に適用されるルールが記載されています。該当するトピックに取り組む前に、対応するリファレンスを読んでください。
| タスク | 参照先 |
|---|---|
| フォルダ、レイヤー、スライス、パブリックAPI、インポート、バレルファイル | references/structure.md |
| FSD 深掘り:決定木、クロスインポート、エンティティ、アセット、auth/API の配置、フレームワークの配線、移行 | references/fsd/(インデックスは structure.md の末尾) |
| 命名、ファイルサイズ、props、デザインシステム、レイアウト、定数 | references/code-style.md |
| コンポーネント、フック、エフェクト、メモ化、トランジション、長いリスト、ハイドレーション | references/hooks-and-rendering.md |
| ウォーターフォール、バンドルサイズ、コード分割、プリロード、再レンダリングコスト | references/performance.md |
| サーバーデータ、クエリ、ミューテーション、ローダー、検索パラメータ、Zustand | references/data-and-state.md |
| フォーム、バリデーション、フォームエラー | references/forms.md |
| セマンティクス、キーボード、フォーカス、コントラスト、ライブリージョン、a11y テスト | references/accessibility.md(React 実装)+ frontend-accessibility スキル(完全なルール、監査) |
| 単体テスト、コンポーネントテスト、E2E テスト、カバレッジ、API モック | references/testing.md |
| ストーリー、Storybook a11y チェック、ビジュアルリグレッション | references/storybook.md |
| XSS、HTML/Markdown/LLM 出力のレンダリング、認証、CSRF、CSP、環境変数、ログアウト | references/security.md |
| Node バージョン、パッケージマネージャー、pre-commit + gitleaks、依存関係監査 | references/tooling.md |
翻訳、言語切り替え、<html lang> | references/i18n.md |
| 新規プロジェクトのセットアップ | references/setup.md |
| フロントエンドのコンテナイメージ | references/docker.md + docker-kubernetes スキル |
スタック
- React 19(
useEffectEventと<Activity>には ≥ 19.2)・ TypeScript strict - レンダリング: Vite SPA または React Router フレームワークモード(SSR + ハイドレーション)。 React Router フレームワークのみ とマークされたルールはその場合のみ適用されます。Next.js、React Server Components、SWR は使用しません。
- ルーティング: TanStack Router または React Router — プロジェクトごとに固定、コードから判別し、混在させない。
- サーバーステート: TanStack Query ・ クライアントステート: Zustand
- フォーム: React Hook Form + Zod または TanStack Form + Zod — プロジェクトごとに固定
- バリデーション: すべての境界(API レスポンス、フォーム、検索パラメータ、環境変数、ストレージ、
postMessage)で Zod を使用 - ツール: Biome(lint + format)、Steiger(FSD ルール)、Vitest + Testing Library、Playwright、Storybook
- React Compiler は使用しない → メモ化は手動で行うが、計測後のみ。
構造 — Feature-Sliced Design
レイヤー app → pages → widgets → features → entities → shared。インポートは下方向のみ。同じレイヤーのスライス間でのインポートは禁止。すべてのスライスは、明示的な名前付きエクスポート(export * は不可)を持つ index.ts を介してのみインポートされます。shared/ui と shared/lib は、コンポーネント/ライブラリごとに 1 つのインデックスを持ちます。app、pages、shared から開始。空のレイヤーフォルダは作成しない。コードは、抽出ルールが成立するまでページに残ります — 現在複数の場所で使用されている、消費者に依存しない独立した変更理由がある、焦点が絞られた責務である。これら 3 つすべてが揃うまでは、重複は許容されます。widgets は推奨されません: 複数のページで再利用され、ページ内に配置できない構成されたブロックにのみ使用します(既存の widget は有効)。詳細: references/structure.md、上流ルールブック references/fsd/rules.md。
- ファイルごとに 1 コンポーネント、ファイルごとに 1 フック、コンポーネントあたり約 150 行。コンポーネントにはスライス名をプレフィックスとして付けます(
references/code-style.md)。 - デザインシステムを優先:
shared/ui/から独自のコンポーネントを使用。shared/ui/の外部からshared/ui/primitives/(UI ライブラリ CLI の出力。編集禁止)をインポートしない。デザインシステムのコンポーネント、props、トークンで何ができるかを確認するまでは、新しい CSS を追加しない。
コンポーネントとフック — 常に
- コンポーネントを別のコンポーネント内で定義しない — レンダリングのたびに再マウントされます。
- 派生させ、同期させない: props や state から計算可能な値は、レンダリング中に計算します。props や state を state にコピーする
useEffectは使用しません。keyを使用してリセットします。 - ユーザーアクションはイベントハンドラ内で実行し、state フラグによってトリガーされるエフェクト内では実行しません。
- エフェクトは外部システムとの同期のみに使用し、依存関係は狭くプリミティブに保ち、エフェクトごとに 1 つの関心事、クリーンアップ関数を記述します。exhaustive-deps ルールを黙らせない。
- 次の値が前の値に依存する場合は
setState(prev => …)を、高コストな初期値にはuseState(() => expensive())を使用します。 - props や state をミューテーションしない:
toSorted()、toReversed()、toSpliced()、with()、スプレッド演算子を使用。 {count > 0 ? <X /> : null}— 左側に数値がある&&は絶対に使用しない。useEffect内でfetchを使用しない。サーバーデータは TanStack Query またはルートローダーから取得します。memo/useMemo/useCallbackは、プロファイリング後、または参照の安定性が必要な場合にのみ使用します。- エラーは決して飲み込まない: 空の
catch、.catch(() => {})は禁止。すべてのルートにエラーバウンダリを、すべてのクエリに loading、error、empty の UI を用意します。
データと state — 常に
- リクエスト関数 → フック → コンポーネント。 HTTP は
shared/api/のクライアントを介してのみ。フックやコンポーネントで HTTP クライアントをインポートしない。 - サーバーデータは TanStack Query のみに存在し、
queryOptionsを使用したクエリキーファクトリを介して管理。レスポンスはリクエスト関数内で Zod を使用してパースします。 - プレーンなリソースアクセス、CRUD、クエリキーファクトリは
shared/api(またはそれのみを使用するページ/機能)に配置。entities/<x>/apiは、そのエンティティのドメインルールやマッピングが必要なリクエストにのみ使用。 - ミューテーションは、影響を受けるすべてのクエリを無効化(または更新)します。
- URL state(フィルター、ソート、ページネーション、タブ)は、型付けされ、Zod でバリデートされた検索パラメータに保持 —
window.locationから手動でパースしない。 - Zustand は共有クライアント state にのみ使用。常にセレクターを使用。永続化ストアは
version、migrate、partializeを使用し、機密データは保持しない。 - ルートデータはルートローダー内で(
queryClient.query({ ...options, staleTime: 'static' })、以前のensureQueryData)、ネストされたコンポーネントで逐次的に取得するのではなく、並列に読み込みます。
アクセシビリティ — 組み込み、後から追加しない
ターゲット: WCAG 2.2 AA。すべてのコンポーネントとページの「完了」の定義の一部。frontend-accessibility スキルに完全なルールと監査手順が含まれています — UI 変更のたびに読み込んでください。
- セマンティック HTML を優先: アクションには
<button>、ナビゲーションには<a>/<Link>、すべてのフィールドには<label>、ランドマーク、1 つの<h1>と順序付けられた見出し。ARIA は、ネイティブ要素が適合しない場合にのみ使用。 - キーボード: すべてのインタラクティブ要素にキーボードで到達可能で操作可能。正の
tabIndexは使用しない。キーボードトラップを作らない。 - すべてのインタラクティブ要素に可視フォーカス(
:focus-visible、≧ 3:1 のコントラスト)。 - フォーカス管理: ルート変更時にページタイトルを設定し、フォーカスを
<h1>または<main>に移動。ダイアログはフォーカスをトラップし、Escapeで閉じ、フォーカスをトリガーに戻す。 - 名前: アイコンボタンには
aria-labelを。画像にはaltを(装飾的な場合はalt="")。 - テキストのコントラスト 4.5:1、UI コンポーネントは 3:1。色だけを唯一のシグナルにしない。
- 動きの低減 (prefers-reduced-motion) を尊重。ライブリージョン(
role="status"/role="alert")を非同期結果とエラーに使用。 - テストはロールとアクセシブルな名前でクエリ。ストーリーは a11y アドオンをパス。重要なページは Playwright で axe をパス。
テスト — 機能と同時に記述し、後回しにしない
- Vitest + Testing Library の単体テストとコンポーネントテストを、すべての機能に対して同じ変更内で実施。UI(ロール、ラベル、
user-event)を通じて振る舞いをテストし、実装の詳細はテストしない。 - カバレッジ ≥ 80% lines かつ ≥ 80% branches、CI で Vitest のしきい値により強制。
- Playwright E2E をすべての重要なユーザーフロー(ログイン、コアビジネスフロー、支払い/チェックアウト、権限)に対して: ロールベースのロケーター、固定待機は使用しない、認証状態を再利用、主要ページで axe スキャンを実行。
- 共有/再利用可能なすべての UI コンポーネントの Storybook ストーリー、すべての状態(デフォルト、ローディング、空、エラー、無効、長いコンテンツ)をカバー。デフォルトはデスクトップビューポート。モバイルビューポートは、プロジェクトがレスポンシブレイアウトを必要とする場合のみ。
- Storybook + Playwright のスクリーンショットによるビジュアルリグレッションテスト。スナップショットテストは控えめに、的を絞ってのみ使用。
セキュリティ — 常に
- DOMPurify なしで
dangerouslySetInnerHTMLを使用しない。React の外ではtextContentを使用し、innerHTMLは決して使用しない。モデル出力と Markdown は生の HTML としてレンダリングしない。 localStorage/sessionStorage/IndexedDB にトークンや機密データを保存しない。 認証はバックエンドが設定するHttpOnlyセッション Cookie を使用。OIDC の場合はバックエンドフォーフロントエンド(BFF)を推奨。- 状態を変更するリクエストは CSRF ヘッダーを送信。
credentials: 'include'は自社の API にのみ使用。 VITE_*変数は公開 — 絶対にシークレットを含めない。?redirect=のターゲットは同一オリジンの相対パスのみ(オープンリダイレクト対策)。- ログアウトは、サーバーセッション、TanStack Query のキャッシュ、およびすべての永続化ストアをクリアします。
- CSP は HTTP ヘッダーとして設定し、
object-src 'none'、base-uri 'none'、frame-ancestorsを含める。 postMessageハンドラーはevent.originをチェック。信頼できないオブジェクトをマージしない(プロトタイプ汚染対策)。- フロントエンドは決して認可を強制しない — バックエンドが行います [ASVS 8.3.1]。
TypeScript と Biome
strictをオンに(TypeScript 7 以降のデフォルト。明示的に維持)。さらにnoUncheckedIndexedAccess、verbatimModuleSyntax、moduleResolution: "bundler"、libはES2023+DOM。TypeScript 7 ではtypesのデフォルトが[]になるため、"vite/client"を明示的にリストアップ。baseUrlは廃止されたのでpathsを使用("@/*": ["./src/*"])。anyは使用禁止(unknownを使用し、型を絞り込む)。エラーを黙らせるための非 null アサーション、@ts-ignoreは禁止(やむを得ない場合は理由を添えて@ts-expect-errorを使用)。マジックナンバーやマジックストリングは禁止。shared/config/または該当スライスのconfig/modelに名前付き定数として定義。- 外部データの型は Zod スキーマから取得(
z.infer/z.output)。手書きのインターフェースは乖離する可能性があるため使用しない。 - Biome: 推奨ルールセットに加えて
noNestedComponentDefinitions、noReExportAll、noConsole(推奨セットには含まれない)を有効化。noBarrelFileはスライスのindex.tsファイルではオフにする(references/structure.md)。 biome ci、tsc --noEmit、steiger ./src、knipがローカルと CI の両方でパスすること。理由のコメントなしにインラインでルールを無効化しない。- コメントは英語。TODO はプロジェクトの TODO マーカーを使用。
リポジトリの衛生管理 — 常に
- Node バージョンを固定:
.nvmrc(LTS)、package.jsonのenginesにengine-strictを設定、packageManagerに正確な npm/pnpm バージョンを指定。ロックファイルをコミット。 - pre-commit をコミットおよびプッシュ用にインストール: ステージングされた変更と未プッシュのすべてのコミットに対して gitleaks を実行、Biome を実行。プッシュ時に
tsc、関連する Vitest テスト、依存関係監査を実行。--no-verifyでバイパスしない。 - CI ですべての MR と定期的に依存関係監査を実行。
詳細: references/tooling.md。
Docker とデプロイ
コンテナイメージ、.dockerignore、Compose、Kubernetes については docker-kubernetes スキルでカバーされています。フロントエンドのワークフローとチェックリストは references/docker.md にあります。すべてのイメージに .dockerignore を配置します。そこで尊重すべきフロントエンド固有の事実:
VITE_*の値はビルド時にバンドルに埋め込まれます。複数の環境に 1 つのイメージを使用するには、ランタイム設定が必要です。コンテナ起動時に環境変数から書き込まれ、アプリ起動時に取得/検証されるconfig.json(またはconfig.js)を提供します — これらも公開情報であり、シークレットは含まれません。- Vite SPA は静的ファイルです。クライアントサイドルートのフォールバックとして
index.htmlを提供するウェブサーバーから配信し、ハッシュ化されたアセットには長期キャッシュ、index.htmlにはno-cache、references/security.mdのセキュリティヘッダーを設定します。 - React Router フレームワークモード(SSR)はランタイムに Node サーバーが必要です(
react-router-serveまたはカスタムサーバー)— 静的デプロイではありません。
記述後
結果のセキュリティレビューには、appsec-review スキルを提案してください。
出典
Vercel Engineering の React ベストプラクティス(github.com/vercel-labs/agent-skills の react-best-practices、MIT ライセンス)を組み合わせ、Next.js から Vite/React Router with TanStack Query に適応(ルール ID はトレーサビリティのために保持)、および OWASP Secure Agent Playbook(CC BY 4.0、OWASP ASVS 5.0)を組み合わせています。構造は Feature-Sliced Design(feature-sliced.design、fsd.how)に従います。references/fsd/ は公式の FSD エージェントスキル(github.com/feature-sliced/skills、MIT ライセンス)から適応されています。アクセシビリティは WCAG 2.2 をターゲットとしています。