react-web-advanced について
react-web-advancedは、TanStack Router、TanStack Start、TanStack Virtualを使うReact Webアプリ向けの実装パターンをまとめたスキルです。Web開発者を対象に、ルーティング、サーバーサイドレンダリング、データ読み込み、大規模リストとReact Queryの連携を扱います。
主な機能
- 01TanStack RouterによるルーティングとURL状態管理
- 02TanStack Startのサーバー関数とSSR
- 03TanStack Virtualによる大規模リストの仮想化
- 04React Queryと連携するルートローダー
- 05コード分割とSuspenseのパターン
活用シーン
- 01型安全なナビゲーションと検索パラメーターの実装
- 02ルート読み込み時のデータ取得と事前取得
- 03大量の項目を含むリストの表示
- 04SSRと認証付きルートフローの設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trancong12102/ agentskills --skill react-web-advancedインストール後、Claude Code で/react-web-advancedを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/better-auth-start.mdintegration.mdrouter.mdssr-auth.mdstart.mdtesting.mdvirtual.md詳細
React Web Advanced:TanStack Router、Start、Virtual
TanStack Router + Start + Virtual スタック上に構築された React アプリ向けの、Web 固有のパターンです。
このスキルは、コアとなるクロスプラットフォームのパターンを扱う react-advanced を拡張します。React Query、XState、Zustand、Zod、TanStack Form、TanStack Table の規約については、まずそちらのスキルをお読みください。
Web アーキテクチャ
Web スタックでは、共通のコアに次の 3 つのレイヤーを追加します。
| レイヤー | ライブラリ | 役割 |
|---|---|---|
| ルーティング + URL の状態 | TanStack Router | 型安全なナビゲーション、検索パラメーター、ルートローダー |
| フルスタック境界 | TanStack Start | サーバー関数(createServerFn)、SSR、ストリーミング |
| 大規模リスト | TanStack Virtual | 1000 件以上の項目の仮想化レンダリング |
黄金律:唯一の信頼できる情報源としての queryOptions
クエリオプションを一度定義し、ローダー、コンポーネント、無効化処理など、あらゆる場所でインポートします。
// queries/posts.ts
export const postsQueryOptions = queryOptions({
queryKey: ["posts"],
queryFn: fetchPosts,
staleTime: 30_000,
});
Router と React Query の連携
Router はコンテキストとして QueryClient を受け取ります。これが唯一の連携ポイントです。
const router = createRouter({
routeTree,
context: { queryClient },
defaultPreload: "intent",
defaultPreloadStaleTime: 0, // Let React Query manage staleness
});
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
defaultPreloadStaleTime: 0 は意図的な設定です。これがないと、Router はローダーの結果を独立してキャッシュするため、プリロード中に React Query の staleTime が無視されます。
ルートローダー + React Query のパターン
ブロッキング処理には ensureQueryData、ノンブロッキング処理には prefetchQuery
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ context: { queryClient }, params }) => {
// Fire-and-forget secondary data
queryClient.prefetchQuery(commentsQueryOptions(params.postId))
// Block route render until critical data is ready
await queryClient.ensureQueryData(postQueryOptions(params.postId))
},
component: PostDetail,
})
function PostDetail() {
const { postId } = Route.useParams()
// Data guaranteed in cache — instant, no loading state
const { data: post } = useSuspenseQuery(postQueryOptions(postId))
return <h1>{post.title}</h1>
}
ウォーターフォールリクエストを避ける
ルートローダーで Promise.all を使い、独立したデータをすべてプリフェッチします。
loader: async ({ context: { queryClient }, params }) => {
await Promise.all([
queryClient.ensureQueryData(userQueryOptions(params.id)),
queryClient.ensureQueryData(permissionsQueryOptions(params.id)),
]);
// Fire-and-forget for non-critical
queryClient.prefetchQuery(activityQueryOptions(params.id));
};
- ルートローダーで取得できるデータを
useEffectで取得しないでください - 親ルートと子ルートのローダーは、デフォルトで並行して実行されます
パフォーマンスパターン
React Compiler(React 19 以降)
コンパイラーを有効にしている場合:
- コンポーネントを手動で
React.memoでラップしないでください - パフォーマンス向上を目的として、手動で
useMemo/useCallbackを使わないでください - 慣用的な React の書き方に従ってください。メモ化はコンパイラーが処理します
- コードが Rules of React に従っていることを確認してください(レンダー中に変更しない)
手動での useMemo / useCallback は、エフェクトの依存関係を制御する場合にのみ引き続き役立ちます。
Suspense 境界の配置
- ルートレベルの境界:ルート定義で
pendingComponent/errorComponentを使用します - ルート内:ノンブロッキングデータを個別に
<Suspense>でラップします - 依存関係のあるクエリは 1 つの
<Suspense>の下にまとめ、同時に解決されるようにします - 独立したクエリには、それぞれ別の
<Suspense>境界を設定します
コード分割
.lazy()または.lazy.tsxファイルを使ってルートを分割します。重要な設定(ローダー、パラメーター)はメインファイルに残し、コンポーネント/UI は遅延読み込みファイルに分割します- 重いオンデマンドコンポーネント(リッチエディター、グラフ)には
React.lazyを使用します - マシン定義は別個の
.tsファイルであるため、自動的に分割されます
ファイル構成
src/
routes/ # TanStack Router file-based routes
__root.tsx # Root layout, router context type
(auth)/ # Route group — no URL impact
(app)/
users/
$userId.tsx
$userId.lazy.tsx # Component-only code split
-components/ # "-" prefix excludes from route tree
queries/ # queryOptions definitions — one file per entity
mutations/ # useMutation wrappers
machines/ # XState machine definitions (pure TS, no React)
stores/ # Zustand stores
serverFns/ # TanStack Start server functions
components/
ui/ # Design system primitives
shared/ # Cross-feature shared components
lib/
query-client.ts # QueryClient singleton
router.ts # Router singleton
test/
setup.ts # Vitest setup
test-utils.tsx # renderWithProviders
mocks/handlers.ts # MSW handlers
主な規約:
- ルート固有のコンポーネントは、ルートツリーに含まれないように
-プレフィックス付きのディレクトリに配置します - URL に影響を与えずに整理するには、パスレスルートグループ
(name)/を使用します .lazy.tsxファイルがエクスポートするのは、component/pendingComponent/errorComponent のみです- テストファイルはソースファイルの隣に配置します(
.test.ts/.test.tsx)
よくある落とし穴
-
createFileRouteのプロパティの順序が間違っている — TypeScript の型推論には、validateSearch -> loaderDeps -> beforeLoad -> loaderの順にする必要があります。create-route-property-orderルールを含む@tanstack/eslint-plugin-routerをインストールしてください。 -
loaderDepsで検索パラメーター全体を返す — パラメーターが変更されるたびにキャッシュが無効になります。 ローダーが使用する依存関係のみを抽出してください。 -
React Query のキャッシュを使わずに
preload="intent"を使用する — ユーザーが 遷移しないと、プリロードされたデータは破棄されます。キャッシュを保持するには、React Query のensureQueryDataと組み合わせてください。 -
ルーターを登録していない —
declare module Registerがないと、すべてがanyになります。 -
<Await>を<Suspense>で囲み忘れる — Suspense の祖先要素がないと、実行時エラーになります。 -
defaultPreloadStaleTimeを 0 に設定していない — Router のデフォルトの staleTime が プリロード時に React Query の staleTime を上書きするため、古いデータが返されます。 -
notFoundComponentでuseLoaderDataを使う — 使用できません。Route.useParams()を使うか、throw notFound({ data: ... })を介してデータを渡してください。 -
認証フローを検証する前にすべてのページを作成する — 機能ページの作成に取りかかる前に、 認証フロー(ログイン → セッション Cookie → 保護されたルートガード → リダイレクト)をまず実装し、 エンドツーエンドで検証してください。認証統合のバグは互いに隠れ合います(7 層以上深くに潜むこともあります)。
references/ssr-auth.mdを参照してください。 -
defaultPreload: "intent"によってホバー時に認証チェックが実行される — intent プリロードでは、 リンクにホバーするたびにbeforeLoadが実行されます。beforeLoadが認証検証を行う場合、ホバーのたびに セッションチェックが実行されます。認証のデバッグ中は intent プリロードを無効にするか、認証チェックを キャッシュ対応にしてください(フェッチする前にqueryClient.getQueryData(sessionKey)を確認します)。 -
invalidateQueries()の無差別な呼び出しが認証フックに連鎖する — キーフィルターを指定せずにqueryClient.invalidateQueries()を呼び出すと、セッションクエリを含む すべてが無効になります。認証のリアクティブフックが再フェッチし、コンポーネントが再レンダリングされ、beforeLoadが再実行されます。常に特定のエンティティクエリキーに絞って無効化してください。 クエリキーの名前空間パターンについては、references/integration.mdを参照してください。 -
認証用 nanostores が API の変更のたびに再レンダリングを引き起こす — Better Auth などの ライブラリは内部で nanostores を使用します。変更が成功するたびにシグナルが切り替わり、 すべての
useSession()/useActiveOrganization()の購読者が同期的に再レンダリングされます。 TanStack Router と組み合わせると、無限の再レンダリングループが発生します。認証フックを React Query の ラッパーに置き換えてください。references/better-auth-start.mdを参照してください。 -
クライアント側のナビゲーションのたびに
beforeLoadが再実行される —beforeLoad内の セッションチェックが React Query のキャッシュなしでサーバー関数を呼び出す場合、リンクをクリックするたびに サーバーへの往復が発生します。キャッシュからデータを返せるよう、staleTimeを指定してensureQueryDataを使用してください。references/ssr-auth.mdを参照してください。
リファレンスファイル
特定のライブラリを使用する場合は、該当するリファレンスファイルを読んでください。
| ファイル | 読むタイミング |
|---|---|
references/router.md | ルーティング、検索パラメーター、ローダー、コード分割、ナビゲーション |
references/start.md | サーバー関数、SSR、ミドルウェア、デプロイ |
references/virtual.md | 仮想化、動的な高さ、無限スクロール、グリッド |
references/integration.md | Router と Query の連携、認証ガード、クエリキーの名前空間、組織データ |
references/ssr-auth.md | SSR Cookie 認証、Vite プロキシ、CORS、ルートガード、FOUC、CF Workers |
references/better-auth-start.md | Better Auth + TanStack Start:シグナル、RQ ラッパー、組織の状態 |
references/testing.md | Router のルートのテスト、renderWithProviders |