frontend-architecture について
ReactとReact Nativeのフロントエンド向けに、特定のフレームワークに依存しない設計を示すスキルです。機能モジュール、インポート境界、スタイルの共置、サーバー状態とUI状態の分離を定め、ライブラリや見た目は指定しません。
主な機能
- 01機能モジュール単位のコード構成
- 02barrelを通じたモジュールAPI公開
- 03ページと画面コードの共置
- 04サーバー状態とUI状態の分離
- 05コンポーネント近くへのスタイル配置
活用シーン
- 01新規Reactアプリの構成設計
- 02Webとネイティブ画面の構成ルール統一
- 03拡大するコードベースのモジュール境界整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill frontend-architectureインストール後、Claude Code で/frontend-architectureを入力して読み込まれたか確認します。
詳細
フロントエンドアーキテクチャ(ポータブル、モジュールベース)
使用する場面
React または React Native のあらゆるフロントエンドで、ポータブルかつフレームワークに依存しないアーキテクチャスタイルが必要なときに、このスキルを使用してください。アプリをページ/画面ディレクトリを持つ機能モジュールに整理し、サーバー状態と UI 状態を厳密に分離し、モジュール間のインポートをバレル経由に限定し、スタイルを同じ場所に配置し、コンポーネントを昇格させる明確なルールを設けます。
ポータブルなスキルです。Claude Code、OpenCode、Codex、Cursor、Windsurf などで読み取れます。 このスキルで説明するのは構造と一連のルールであり、コンポーネントライブラリ、状態管理ライブラリ、ビジュアルスタイルではありません。 意図的にグローバルな設計になっています。同じモジュール/ページ/状態モデルを Next.js(App Router)、React + Vite(SPA)、Remix、Expo / React Nativeに適用でき、 あらゆる状態管理およびスタイリングスタックで機能します。
目標は、すべてのコントリビューターが誰にも尋ねずに、次の3つの問いにすぐ答えられるコードベースです。 「このコードはどこに置くのか?」、「何が何をインポートしてよいのか?」、「これはサーバー状態か、UI 状態か?」 この構造によって、答えが明確になります。
0. 5つの基本的な考え方
- 機能モジュールはそれぞれの領域を所有します。 各機能は、ページ、コンポーネント、フック、状態、型、単一の公開バレルを備えた、自己完結型の
modules/{feature}/フォルダーです。 - ページ/画面はファイルではなくディレクトリです。 ルートはフォルダーであり、コンポーネント、スタイル、そのルート専用のコンポーネント/フックを同じ場所にまとめます。
- 状態は由来によって分けます。 サーバーデータはクエリ/キャッシュレイヤーに置きます。UI/クライアント状態はストアに置きます。権威あるリモート状態とローカルの下書き/楽観的な投影を区別し、明示的に整合させます。
- 境界を越えるインポートはバレル経由に限ります。 他のモジュールの内部に立ち入ることは禁止です。
@/modules/{feature}からインポートし、それより深いパスは使いません。 - コードはあらかじめ配置するのではなく、昇格させます。 可能な限りローカルな場所から始め、2つ目の利用箇所ができた場合にのみ、より外側へ移動します。
以下はすべて、この5つの考え方を機械的に適用したものです。特定のライブラリには依存しません。使用するスタックはセクション4と6で選んでください。
1. ディレクトリ構成
構成はフレームワーク間で同一です。異なるのは上に重ねるルーティングレイヤーだけです(セクション7を参照)。
src/
├── app/ or routes/ or navigation/ ← framework routing layer (thin — see §7)
├── modules/ ← feature modules (the heart of the app)
│ └── {feature}/
│ ├── index.ts ← PUBLIC BARREL — the only cross-module entry point
│ ├── README.md ← what this module owns, its routes, its data deps
│ ├── components/ ← components reused by 2+ pages IN THIS MODULE
│ ├── pages/ ← page/screen directories (one per route)
│ │ └── {page}/
│ │ ├── {page}.tsx ← the page/screen component
│ │ ├── {page}.styles.ts ← ALL styling for this page
│ │ ├── index.ts ← re-exports the page component
│ │ ├── components/ ← components used ONLY by this page
│ │ ├── hooks/ ← hooks used ONLY by this page
│ │ ├── constants/
│ │ └── README.md ← route, params, permissions, data deps
│ ├── hooks/ ← data hooks (query/mutation) + module hooks
│ ├── stores/ ← UI/client state store(s) — never server data
│ ├── services/ ← data-access (API calls) for this feature
│ ├── utils/ ← pure module utilities (co-located *.test.ts)
│ ├── constants/
│ └── types/ ← module request/response + view-model types
└── shared/ ← cross-module building blocks
├── components/ ← components used by 2+ MODULES
├── hooks/ ← cross-cutting hooks
├── api-client/ ← one typed client; a shared transport contract; server-only and client-safe implementations must remain separate
├── store/ ← root store wiring (if your state lib needs one — see §4)
├── utils/ ← formatters, cn()/clsx, helpers
├── constants/
└── types/
スキャフォールド作成時に空になる可能性のあるフォルダーには、最初から構造が見えるよう、.gitkeep を配置します。
2. 機能モジュール
モジュールは、製品の垂直方向の一部分です(例:auth、billing、dashboard、settings)。その機能に必要なものをすべて含み、意図的に小さく絞った公開インターフェースを提供します。
2.1 バレル(index.ts)が契約です
modules/{feature}/index.ts は、他のモジュールおよびルーティングレイヤーがインポートできる唯一のものです。次のものを再エクスポートします。
- ルーターがマウントするページ/画面コンポーネント。
- 他の機能が正当に必要とするデータフック。
- ストアフック/スライスと、その公開型。
- 他の機能が依存する共有定数/型。
// CORRECT — consume the public surface
import { InvoiceListPage, useInvoiceList } from "@/modules/invoice";
// WRONG — reaching into internals couples you to private structure
import { InvoiceListPage } from "@/modules/invoice/pages/invoice-list/invoice-list";
バレルに含めるものは厳選してください。エクスポートされていないものは、意図的に非公開です。エクスポートは短いコメント(ページ、フック、ストア、型)でグループ化してください。将来の読者は、モジュールの API ドキュメントとしてバレルを参照します。
2.2 1つのモジュール=意図的に定義された1つの機能コンテキスト
utils モジュールや components モジュールは作成しないでください。モジュールは技術レイヤーではなく、製品の機能に対応します。技術的な構成要素は shared/ に置きます。
2.3 モジュールの README
各モジュールの README.md には、モジュールが所有するもの、そのページを表示するルート、データ依存関係(エンドポイント/フック)、モジュール間のルールを記載します。これは新しいコントリビューターが最初に読むものです。
3. ディレクトリとしてのページ/画面
ページとは、ルーターがマウントするルート(React Native における「画面」)です。単一のコンポーネントとして始める場合でも、必ずフォルダーにし、単独のファイルにはしません。これにより、ページが成長しても適切な場所に収まります。ページにサブコンポーネントやフックが必要になったとき、すでにそれらを置く場所があるためです。
pages/{page}/
├── {page}.tsx ← the page/screen component
├── {page}.styles.ts ← every style for this page (no inline styles — see §5)
├── index.ts ← export { PageComponent } from "./{page}"
├── components/ ← used ONLY by this page
├── hooks/ ← used ONLY by this page
├── constants/
└── README.md ← route, params, permissions, data deps
ページの README は簡潔で要点を押さえたものにします。ルートパス、想定されるパラメーター、必要な権限や認証、依存するフックを記載します。これは、ページとアプリのその他の部分との間の契約です。
最初からフォルダーにする理由: 1 つのファイルから始めたページは、やがてサブ行コンポーネント、派生合計値のフック、スタイルファイルが必要になります。ページがファイルの場合、それらは場当たり的な場所に置かれてしまいます。ページがフォルダーなら、置き場所が明確になり、差分も読みやすく保てます。
4. 状態:発生元ごとに分ける(必須、ライブラリに依存しない)
状態には 2 種類あり、それぞれに適切な置き場所があります。これらを混在させることは、このスキルが防ごうとしている最も一般的なアーキテクチャ上の失敗です。分離は必須ですが、使用するライブラリは自由です。
| 状態の種類 | 例 | 配置場所 |
|---|---|---|
| サーバー状態 | 取得したエンティティ、リスト、集計値など、API が管理するすべてのデータ | クエリ/キャッシュレイヤー(TanStack Query、RTK Query、SWR、Apollo など) |
| UI/クライアント状態 | 開いているダイアログ、テーブルのフィルター/並べ替え、ウィザードのステップ、入力中の下書き、プレビューの切り替え | クライアントストア(Zustand、Redux Toolkit、MobX、Jotai、Valtio、React Context など) |
4.1 厳守するルール(ライブラリに依存しない)
- サーバーのレスポンスをクライアントストアに複製してはいけません。 取得したエンティティを Zustand/Redux/MobX にコピーしてはいけません。クエリ/キャッシュレイヤーがサーバーデータの唯一の信頼できる情報源です。
- クライアントコンポーネントではデータフックを優先します。サーバーコンポーネントやフレームワークのローダーは、サーバー専用サービスを通じてデータを取得してもかまいません。 コンポーネントは、サーバーデータをデータフックから、UI 状態をストアセレクターから読み取ります。ネットワーククライアントを直接呼び出してはいけません。
- 連続的に変化する値を再レンダリングの状態で管理してはいけません。 スクロールの進行度、ポインターの位置、ドラッグのオフセットには、レンダー状態ではなく ref/アニメーション値を使用します(レンダー状態ではフレームごとにツリーが再レンダリングされます)。
- モジュールごとにストアの境界を 1 つにします。 どのライブラリを使う場合でも、各モジュールにまとまりのあるストア単位(Zustand フック、Redux スライス、MobX クラス、Jotai アトムのグループ)を 1 つ用意し、モジュールのバレル経由でアクセスします。不要な再レンダリングを避けるため、コンポーネントは必要最小限のスライスだけを購読します。
4.2 クライアント状態ライブラリの選択 — 同じ構造、異なる構文
プロジェクトごとに 1 つ選び、一貫して使います。どのライブラリも「モジュールごとにストア単位を 1 つ」という構成に無理なく対応できます。I インターフェース命名規則に注意してください。状態インターフェースには I を接頭辞として付けます(例:IFeatureUiState)。
Zustand — modules/{feature}/stores/{feature}.store.ts
import { create } from "zustand";
export interface IFeatureUiState {
isPreviewOpen: boolean;
filter: string;
togglePreview: () => void;
setFilter: (filter: string) => void;
reset: () => void;
}
const INITIAL_STATE = { isPreviewOpen: false, filter: "" } as const;
export const useFeatureUiStore = create<IFeatureUiState>()((set) => ({
...INITIAL_STATE,
togglePreview: () => set((s) => ({ isPreviewOpen: !s.isPreviewOpen })),
setFilter: (filter) => set({ filter }),
reset: () => set({ ...INITIAL_STATE }),
}));
Redux Toolkit — modules/{feature}/stores/{feature}.slice.ts(shared/store/ に登録)
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
export interface IFeatureUiState {
isPreviewOpen: boolean;
filter: string;
}
const initialState: IFeatureUiState = { isPreviewOpen: false, filter: "" };
export const featureUiSlice = createSlice({
name: "featureUi",
initialState,
reducers: {
togglePreview: (s) => {
s.isPreviewOpen = !s.isPreviewOpen;
},
setFilter: (s, action: PayloadAction<string>) => {
s.filter = action.payload;
},
reset: () => initialState,
},
});
MobX — modules/{feature}/stores/{feature}.store.ts
import { makeAutoObservable } from "mobx";
export interface IFeatureUiState {
isPreviewOpen: boolean;
filter: string;
}
export class FeatureUiStore implements IFeatureUiState {
isPreviewOpen = false;
filter = "";
constructor() {
makeAutoObservable(this);
}
togglePreview = () => {
this.isPreviewOpen = !this.isPreviewOpen;
};
setFilter = (filter: string) => {
this.filter = filter;
};
reset = () => {
this.isPreviewOpen = false;
this.filter = "";
};
}
Jotai — modules/{feature}/stores/{feature}.atoms.ts
import { atom } from "jotai";
export const isPreviewOpenAtom = atom(false);
export const filterAtom = atom("");
どのライブラリを選んでも、§4.1 のルールは変えないでください。このスキルが重視するのは、サーバー状態と UI 状態が分離され、各モジュールが 1 つのストア単位を所有することです。どのライブラリを使って状態を管理するかではありません。
4.3 データレイヤー(サーバーの状態)
ネットワークアクセスはすべて、shared/api-client/ にある単一の型付きクライアントを通じて行います。モジュールではこれをクエリ/ミューテーションのフックとキーのファクトリーでラップし、キャッシュと無効化の一貫性を保ちます。
// modules/invoice/hooks/invoiceKeys.ts — hierarchical key factory (TanStack Query style)
export const invoiceKeys = {
all: ["invoices"] as const,
lists: () => [...invoiceKeys.all, "list"] as const,
list: (params: IListParams) => [...invoiceKeys.lists(), params] as const,
details: () => [...invoiceKeys.all, "detail"] as const,
detail: (id: string) => [...invoiceKeys.details(), id] as const,
} as const;
lists() を無効化すると、フィルターされたすべてのページが更新されます。detail(id) は1つのエンティティを対象にします(RTK Query/SWR/Apollo では、タグ/キーを使って同じ考え方を表現します)。コンポーネントで生の fetch() を呼び出すことはありません。useInvoiceList()/useCreateInvoice() を呼び出します。
5. スタイリング:同じ場所に配置し、インラインスタイルを使わない(スタイリングライブラリに依存しない)
スタイリングは JSX の中にもコンポーネント本体の中にも記述しません。各ページまたはコンポーネントには、同じ場所に配置したスタイルファイルを用意します。このルールは常に同じですが、構文はスタイリングスタックに従います。
- Tailwind(Web):
{name}.styles.tsから、cn()(clsx + tailwind-merge)で組み合わせた名前付きクラス文字列をエクスポートし、バリアントにはcvaを使います。JSX ではstyles.headerを参照します。 - CSS Modules/vanilla-extract: 同じ場所に
{name}.module.css/{name}.css.tsを配置します。JSX ではstyles.headerを参照します。 - styled-components/Emotion: 同じ場所に
{name}.styles.tsを配置し、スタイル付きコンポーネントをエクスポートします。 - Tamagui(Web + ネイティブ): 同じ場所に
{name}.styles.tsを配置し、styled(...)コンポーネント、またはcreateStyledContext/useStyleのトークンセットをエクスポートします。Tamagui のトークン($background、$space.4)を参照し、値をインラインでハードコードすることは避けてください。1つのコードベースからWeb と React Native の両方を対象にする場合は、Tamagui を推奨します。 - React Native StyleSheet/Nativewind: 同じ場所に
{name}.styles.tsを配置し、StyleSheet.create({...})(または Nativewind のクラス名)をエクスポートします。JSX ではstyles.headerを参照します。
// invoice-list.styles.ts (Tailwind example)
export const invoiceListStyles = {
page: "flex flex-col gap-8",
header: "flex flex-col gap-1.5",
title: "text-3xl font-semibold tracking-tight",
} as const;
// invoice-list.styles.ts (Tamagui example — works on web AND native)
import { styled, YStack, Text } from "tamagui";
export const InvoiceListPage = styled(YStack, { flex: 1, gap: "$8" });
export const InvoiceListHeader = styled(YStack, { gap: "$1.5" });
export const InvoiceListTitle = styled(Text, {
fontSize: "$8",
fontWeight: "600",
});
どのスタックでも、コンポーネント本体にインラインの style={{...}} リテラルを記述しないでください。理由:スタイリングをインラインで記述すると、ばらつきや重複が生じます。同じ場所に配置したスタイルファイルがあれば、画面ごとの余白のリズム、テーマの正確性、レスポンシブな動作を一か所で確認できます。アクセントの固定やブレークポイントなど、分かりにくい選択については、そこでコメントに記録してください。
このスキルは、_ビジュアル_デザインを規定するものではありません。そのためには、デザイン/コンポーネントのスキルと組み合わせてください。ここで規定するのは、_スタイリングを配置する場所_のみです。
6. 命名規則
一貫した命名により、構造がひと目で分かるようになります。
- インターフェース名には
Iを付けます —IFeatureUiState、IInvoiceListParams、IUserProfile。型エイリアス(ユニオン型、マップ型、プリミティブ型)にはIを付けません(type SortDirection = "asc" | "desc")。 - コンポーネント: ファイル名とエクスポート名には
PascalCaseを使います —InvoiceListPage.tsx、LineItemRow.tsx。 - ページ/画面: ディレクトリ名には
kebab-caseを使い、コンポーネントのファイル名も同じ名前にします —pages/invoice-list/invoice-list.tsx。 - フック:
useCamelCaseを使います —useInvoiceList、useFeatureUiStore。 - ストア: Zustand/MobX では
{feature}.store.ts、Redux では{feature}.slice.ts、Jotai では{feature}.atoms.tsを使います。フック名はuse{Feature}{Purpose}Storeとします。 - スタイル: 所有するコンポーネントと同じ場所に
{name}.styles.tsを配置します。 - 定数: 値には
SCREAMING_SNAKE_CASE、ファイル名にはkebab-caseまたはcamelCaseを使います。 - バレル: 常に
index.tsを使います。
7. フレームワークアダプター
モジュール/ページ/状態のモデルは常に同じです。変わるのは、その上にある薄いルーティングレイヤーだけです。ページは常に modules/ に配置し、ルーティングレイヤーはそれをマウントするだけです。
7.1 Next.js(App Router)
src/app/には、レイアウト/認証の境界を定めるルートセグメントとルートグループ((marketing)、(app)、(public))を配置します。ルートファイルは薄く保ちます。モジュールのバレルからページコンポーネントをインポートしてレンダリングします。- サーバーコンポーネントをデフォルトとし、操作が必要なリーフには
"use client"を指定します。プロバイダー(クエリクライアント、ストア、テーマ)は"use client"の境界内に配置します。
// app/(app)/invoices/page.tsx — thin route file
import { InvoiceListPage } from "@/modules/invoice";
export default function Page() {
return <InvoiceListPage />;
}
7.2 React + Vite(SPA)
src/routes/(または単一のrouter.tsx)でルートテーブル(React Router/TanStack Router)を宣言し、パスをモジュールのページコンポーネントに対応付けます。すべてクライアント側で動作します。アプリのルートでツリー全体をクエリクライアントとストア/テーマのプロバイダーで一度ラップします。
7.3 Remix
app/routes/内のルートモジュールは薄いままにし、モジュールのページコンポーネントを再エクスポート/マウントします。ローダー/アクションはモジュールのservices/に処理を委譲します。モジュールの境界は変更しません。
7.4 Expo / React Native
- ルーティングには Expo Router(ファイルベースで、
app/内)または React Navigation(navigation/)を使用します。ルート/画面ファイルは薄く保ち、画面コンポーネントをモジュールのバレルからインポートします。 - 「ページ」は「画面」です。同じディレクトリパターンを使用します:
pages/{screen}/{screen}.tsx+{screen}.styles.ts。 - クエリレイヤーとクライアントストアはそのまま動作します(TanStack Query、Zustand、Redux、MobX、Jotai はいずれも RN で利用できます)。型付きのクライアント契約は共有できますが、ブラウザー/RN のトランスポート、ストレージ、Cookie、認証にはプラットフォーム固有のアダプターが必要です。
- スタイリングには Tamagui(Web とネイティブの共有に推奨)、
StyleSheet、または Nativewind を使用します。モジュールのロジックに DOM を含めないでください。
// app/invoices/index.tsx (Expo Router) — thin screen file
import { InvoiceListScreen } from "@/modules/invoice";
export default InvoiceListScreen;
7.5 Web とネイティブ間での共有
Web と Expo の両方を対象とする場合は、フレームワークに依存しないコード(型、バリデーター、フォーマッター、API クライアント契約)を、両方のアプリが利用する共有パッケージにまとめます。また、両方でレンダリングする必要があるコンポーネントには Tamagui を優先してください。モジュールの境界はどちらでも同じです。
8. 規約のチェックリスト(レビューで適用)
- 新機能 →
shared/にファイルを分散させず、新しいmodules/{feature}/を作成し、index.ts+README.mdを含めます。 - 新しいルート → 単独のファイルではなく、ページ/画面ディレクトリ(
{page}.tsx+{page}.styles.ts+index.ts+README.md)を作成します。 - モジュール間のインポートにはバレル(
@/modules/{feature})を使用し、内部の深いパスを直接指定しません。 - サーバーデータはクエリ/キャッシュレイヤーに、UI の状態はモジュールストアに置きます。どちらの状態も互いに漏れ出さないようにします(ライブラリの選択を問わず)。
- コンポーネント内で
fetch()を使用せず、共有クライアントを基盤とする型付きデータフックのみを使用します。 - インラインスタイルを使用せず、同じ場所に配置した
{name}.styles.ts(Tailwind/CSS Modules/Tamagui/StyleSheet/styled-components)を使用します。 - コンポーネント/フック/ユーティリティは最も限定的なスコープに配置し、2つ目の利用箇所が現れた場合にのみ上位へ移動します。
- モジュールごとにストアユニットを1つ用意し、セレクターと
resetを備え、バレル経由でアクセスします。 - インターフェースには
Iプレフィックスを使用します。コンポーネント/フック/ファイルは §6 に従います。 - クエリーキー/タグはモジュールごとのファクトリーから取得し、無効化は階層的に行います。
- ルーティングファイルは薄く保ち、モジュールのページをマウントし、レイアウト/認証の境界のみを管理します。
- ルート、パラメーター、データ依存関係を変更した場合は、モジュール/ページの README を更新します。
9. コンポーネントの昇格(ローカルから始め、外側へ移動)
コンポーネントは、それを使用する最も限定的なスコープに作成し、2つ目の利用箇所が現れた場合にのみ昇格させます。「再利用されるかもしれない」という理由で、あらかじめコンポーネントを配置しないでください。
| コンポーネントの利用箇所… | 配置先 | インポート方法 |
|---|---|---|
| 1つのページのみ | pages/{page}/components/ | ページ内の相対パス |
| 1つのモジュール内の2つ以上のページ | modules/{feature}/components/ | @/modules/{feature}(バレル経由) |
| 2つ以上のモジュール | shared/components/ | @/shared/... |
| 2つ以上のアプリ/リポジトリ | 公開されたデザインシステムパッケージ | パッケージ名 |
同じ段階的な考え方は、フック、ユーティリティ、定数にも適用されます:ローカル → モジュール → 共有 → パッケージ。昇格は意図的な移動(インポート箇所の更新を含む)であり、事前の憶測で決めるものではありません。
10. このスキルの適用方法
新しいアプリの足場を作る: src/modules/、src/shared/、およびフレームワークのルーティングレイヤー(§7)を作成します。共有の api-client、クエリレイヤー、選択したクライアントストアプロバイダーを追加します。最初のモジュールにストアのテンプレートを配置します。
機能を追加する: サブフォルダー一式(pages/ components/ hooks/ stores/ services/ utils/ constants/ types/)、厳選した index.ts、および README.md を含む modules/{feature}/ を作成します。最初の画面はページディレクトリとして構築します。
コードの配置先を判断する: 「これを利用するのは誰か?」と問いかけます。最も範囲の狭いものを選びます(§9)。「このデータはどこから来たのか?」と問いかけます。サーバー由来ならクエリレイヤー、UI 由来ならストアです(§4)。
構成をレビューする: §8 のチェックリストを実行します。特に重要なのは、状態の起源の漏れ(サーバーデータをクライアントストアに置くこと)と、モジュール間の深いインポート(バレルを迂回すること)です。どちらもアーキテクチャを最も早く損ないます。
このスキルの公開/インストール
このスキルは Anthropic の SKILL.md 形式に準拠しており、さまざまなエージェントで利用できます。インストールと検索が可能になるようにするには(例:skills.sh / npx skills):
- このフォルダーを、公開 GitHub リポジトリ内の
skills/ディレクトリに配置します(skills/frontend-architecture/SKILL.mdのようなパス)。 - フロントマターの
nameと、要点が明確なdescription(上記)を維持します。検索インデックスは、この説明を基に照合します。 - 任意のプロジェクトから次のコマンドでインストールします:
npx skills add <org>/<repo> --skill "frontend-architecture"。 SKILL.mdに対応していないエージェントには、AGENTS.md/CLAUDE.mdからこのスキルを参照させることができます。Kiro では、ステアリングファイルとして複製できます。
制限事項
- タスクが、このスキルの上流ソースおよびローカルプロジェクトのコンテキストと明確に一致する場合にのみ、このスキルを使用してください。
- 変更を適用する前に、コマンド、生成コード、依存関係、認証情報、外部サービスの動作を確認してください。
- 例を、環境固有のテスト、セキュリティレビュー、または破壊的もしくは高コストな操作に対するユーザーの承認の代わりとして扱わないでください。
ランタイムと分離に関する例外
ここに記載するのは選択された規約であり、すべての React フレームワークが同じように動作することを保証するものではありません。小さなタスクのために全面的な再構成を強いるのではなく、既存のルーティング/ビルド規約を維持してください。サーバー専用のエクスポートは、クライアントフック/コンポーネントから分離します。混在したバレルを使うと、認証情報やサーバー依存関係がクライアント境界に取り込まれたり、RSC の規則に違反したりする可能性があります。適切な場合は型のみのエクスポートを使用し、インポートの循環とツリーシェイキングを確認してください。SSR のクエリ/ストアインスタンスは、プロセス全体で共有されるユーザーキャッシュ/ストアではなく、リクエストごと、かつ認証済みの ID ごとに作成します。テナント/ユーザー/権限と関連するフィルターをキャッシュキーに含め、ログアウト時や ID の変更時にはクリアまたは再検証します。URL フィルターや分離されたコンポーネント状態を必ずしもグローバルストアにする必要はありません。編集可能な下書き/楽観的な変更には、権威あるサーバー状態を重複させるのではなく、明示的な競合/調整ポリシーが必要です。共有ネットワーク契約は、サーバー側の認可やプラットフォーム固有の安全な認証情報ストレージの代わりにはなりません。ブラウザー/RN API、アクセシビリティ、スタイリング、ナビゲーションは個別に検証する必要があります。I 接頭辞やバレルはセキュリティ境界ではありません。外部スキルの公開/インストールは、別途許可が必要な操作であり、ここで示すレイアウトの考え方を適用するための前提条件ではありません。