Communitygithub.com

Reactパフォーマンス

Vercelエンジニアリングから派生したReactおよびNext.jsのパフォーマンス最適化パターン集

Reactパフォーマンス とは?

このスキルは、React 18/19およびNext.js向けのパフォーマンス最適化パターンを提供します。Vercel Labsの`react-best-practices`をベースに、優先順位付きのルールと決定木ガイダンスに従って、アクティブなコードレビューとリファクタリングを支援します。主なカテゴリは8つあり、ウォーターフォールの解消(最重要)、バンドルサイズ最適化(最重要)、サーバーサイドパフォーマンス(高)、クライアントサイドデータ取得(中高)、再レンダリング最適化(中)、レンダリングパフォーマンス(中)、JavaScriptパフォーマンス(低中)、高度なパターン(低)です。このスキルはClaude Code、Coder、Cursorなど、SKILL.mdファイルをサポートする任意のAIエージェントプラットフォームで使用でき、コードレビューやPRでのパフォーマンス関連の判断を効率的に行えるようにします。各ルールには具体的なコード例と、間違ったパターンと正しいパターンが示されており、開発者がすぐに実践できる実用的なアドバイスを提供します。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/react-performance

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Reactパフォーマンス は何をしますか?

React 18/19およびNext.js向けのパフォーマンス最適化パターン集。Vercel Labs react-best-practices (MIT, v1.0.0) を基に作成されています。このスキルは優先順位に従ってルールを整理し、アクティブなコードレビューとリファクタリングのための決定木ガイダンスを提供します。

アクティベートするタイミング

  • React/Next.jsのコードをパフォーマンス面から作成またはレビューする場合
  • ページの読み込みが遅い、インタラクションが遅い、クライアントのCPU使用率が高いなどの問題を診断する場合
  • バンドルサイズやLighthouseのCore Web Vitalsの低下を監査する場合
  • Server Components / APIルートでのウォーターフォールを解消する場合
  • クライアントサイドの再レンダリングを削減する場合
  • 長いリスト、アニメーション、ハイドレーションを最適化する場合
  • app/、pages/、components/、データレイヤーに影響するPRでの最適化の選択を監査する場合

優先順位インデックス

優先順位カテゴリプレフィックス重要となるタイミング
1 — 最重要ウォーターフォールの解消async-awaitの後に独立したawaitが続く場合
2 — 最重要バンドルサイズ最適化bundle-初回読み込みJS、ルートレベルのインポート、サードパーティライブラリ
3 — 高サーバーサイドパフォーマンスserver-RSC、Server Actions、APIルート、SSR
4 — 中高クライアントサイドデータ取得client-SWR / TanStack Query / フック内の生のfetch
5 — 中再レンダリング最適化rerender-高頻度の状態更新、親子間のファンアウト
6 — 中レンダリングパフォーマンスrendering-長いリスト、アニメーション、ハイドレーション
7 — 低中JavaScriptパフォーマンスjs-ホットループ、頻繁なアロケーション
8 — 低高度なパターンadvanced-エフェクトイベント統合、安定したref

1. ウォーターフォールの解消(最重要)

「ウォーターフォールはパフォーマンスキラーの第1位」 — 逐次的なawaitはすべてネットワークレイテンシを追加します。

awaitの前に安価な条件をチェック

リモートデータを待つ前に、同期的な条件(props、env、ハードコードされたフラグ)をチェックします。

// 誤り
async function Page({ id }: { id: string }) {
  const flag = await getFlag("show-page");
  if (!flag || !id) return null;
  const data = await getData(id);
  // ...
}

// 正しい — 安価な同期条件で先にショートサーキット
async function Page({ id }: { id: string }) {
  if (!id) return null;
  const flag = await getFlag("show-page");
  if (!flag) return null;
  const data = await getData(id);
}

使用時までawaitを遅延させる

awaitを、その値を使用するブランチに移動します。

// 誤り — データが必要かどうかを判断する前にawaitしている
const user = await getUser(id);
if (mode === "guest") return renderGuest();
return renderUser(user);

// 正しい
if (mode === "guest") return renderGuest();
const user = await getUser(id);
return renderUser(user);

独立した処理にはPromise.all

// 誤り — 逐次処理
const user = await getUser(id);
const posts = await getPosts(id);
const followers = await getFollowers(id);

// 正しい — 並列処理
const [user, posts, followers] = await Promise.all([
  getUser(id),
  getPosts(id),
  getFollowers(id),
]);

部分的な依存関係 — 早期に開始、遅れてawait

// 正しい — すべてのPromiseを開始し、各結果が必要になった時点でのみawait
const userP = getUser(id);
const postsP = getPosts(id);
const profile = await getProfile(id);
if (profile.private) return null;
const [user, posts] = await Promise.all([userP, postsP]);

ストリーミングのためのSuspense

<Suspense>の境界をデータの近くに配置することで、遅いサブツリーがストリーミングされている間も、ページは可能な限りの内容を描画できます。トレードオフ:コンテンツが到着したときのレイアウトシフト — スペースを予約します(スケルトンまたはmin-height)。

Server Components: コンポジションによる並列化

// 誤り — 1つのコンポーネント内で兄弟のawaitが逐次的に実行される
export default async function Page() {
  const user = await getUser();
  const cart = await getCart();
  return <View user={user} cart={cart} />;
}

// 正しい — 子コンポーネントに分割し、Reactが並列実行する
export default async function Page() {
  return (
    <View>
      <UserSection />
      <CartSection />
    </View>
  );
}

2. バンドルサイズ最適化(最重要)

直接インポート、バレルは使わない

バレルindex.tsファイルを使用すると、ツリーシェイキングがほとんどのコードを削除する場合でも、バンドラーはモジュールグラフ全体を辿る必要があります。直接インポートにより、多くの実世界のアプリで最初の読み込みJSが200〜800ms節約されます。

// 誤り
import { Button, Card, Modal } from "@/components";

// 正しい
import { Button } from "@/components/Button";
import { Card } from "@/components/Card";
import { Modal } from "@/components/Modal";

Next.js 13.5以降にはOptimize Package Importsがあり、リストされたパッケージに対してこれを自動化します — これを使用してください。リストされていないライブラリには手動での直接インポートが引き続き必要です。

静的に分析可能なパス

// 誤り — バンドラー/トレース分析を無効化
const mod = await import(`./pages/${name}`);

// 正しい — 分岐ごとに明示的
const mod = name === "home" ? await import("./pages/home") : await import("./pages/about");

重いコンポーネントの動的インポート

import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("./HeavyChart"), {
  loading: () => <Skeleton />,
  ssr: false, // クライアント専用の場合
});

サードパーティスクリプトの遅延読み込み

アナリティクス、ログ、サポートウィジェットはハイドレーション後に読み込みます。strategy="afterInteractive"(デフォルト)または"lazyOnload"を指定してnext/scriptを使用します。

条件付きモジュール読み込み

if (user.role === "admin") {
  const { AdminPanel } = await import("./admin/AdminPanel");
  // ...
}

ホバー/フォーカス時のプリロード

ホバー時に<link rel="preload">またはimport()をトリガーして、ユーザーがクリックするまでにバンドルがキャッシュに存在するようにします。

3. サーバーサイドパフォーマンス(高)

Server ActionsをAPIルートと同様に認証する

すべての"use server"関数は公開エンドポイントです。アクション内で認証と認可の両方を行ってください — 呼び出し元のClient Componentのゲーティングに依存しないでください。

"use server";
export async function deleteUser(formData: FormData) {
  const session = await getSession();
  if (!session?.user) throw new Error("Unauthorized");
  const targetId = String(formData.get("id"));
  if (session.user.role !== "admin" && session.user.id !== targetId) {
    throw new Error("Forbidden");
  }
  await db.user.delete({ where: { id: targetId } });
}

React.cache()によるリクエスト単位の重複排除

import { cache } from "react";

export const getUser = cache(async (id: string) => {
  return db.user.findUnique({ where: { id } });
});

React.cacheは単一のリクエスト内で重複排除を行います。同じレンダリング内の3つのServer ComponentsからgetUser("1")を呼び出す = 1回のDBクエリ。

複数リクエスト間のデータにはLRUキャッシュ

リクエストごとに変化しないデータ(設定、ルックアップテーブル)には、React外部でLRUキャッシュまたはunstable_cacheを使用してキャッシュします。

RSCプロップでの重複シリアライゼーションを回避

Server Componentが複数のClient Componentに同じデータをレンダリングする場合、データはコンシューマごとに一度ずつシリアライズされます。Client Componentを引き上げて、子を渡すようにします。

静的なI/Oをモジュールスコープに移動

// 正しい — モジュール読み込み時に1回だけ実行
const fontData = readFileSync(fontPath);

export async function Page() {
  return <Banner font={fontData} />;
}

RSC/SSRでミュータブルなモジュールレベルの状態を持たない

サーバー上のモジュール状態はすべてのリクエスト間で共有されます — ユーザー間で競合状態が発生します。代わりにリクエストスコープのストレージ(headers()、cookies()、非同期コンテキスト)を使用します。

Client Componentに渡すデータを最小化

クライアントが必要とするものだけをシリアライズします。フィールドを削除し、ページネーションし、DBレイヤーで列をプロジェクションします。

アイテムごとにPromise.allでネストされたfetchを並列化

const users = await getUsers();
const enriched = await Promise.all(
  users.map(async (u) => ({ ...u, posts: await getPostsFor(u.id) })),
);

非ブロッキング処理にafter()を使用

Next.js 15のafter()は、レスポンスが送信された後に処理を実行します — ログ記録、キャッシュウォーミング、アナリティクスなど。

import { after } from "next/server";
export async function GET() {
  const data = await getData();
  after(() => logAnalytics(data));
  return Response.json(data);
}

4. クライアントサイドデータ取得(中高)

SWR / TanStack Queryを使用した重複排除

複数のコンポーネントがuseUser(id)を呼び出す場合、1つのネットワークリクエストと1つのキャッシュエントリを共有する必要があります。SWRまたはTanStack Queryを使用してください — 共有データに独自のuseEffect + fetchを決して実装しないでください。

グローバルイベントリスナーの重複排除

// 誤り — すべてのコンポーネントが独自のリスナーを追加
useEffect(() => {
  window.addEventListener("scroll", handler);
  return () => window.removeEventListener("scroll", handler);
}, []);

// 正しい — フックとグローバルサブジェクトによる単一の共有リスナー
const useScroll = createScrollHook(); // 内部的にはシングルトンサブジェクト

スクロール用のパッシブリスナー

window.addEventListener("scroll", handler, { passive: true });

スクロールの滑らかさが向上します。リスナーはpreventDefault()を呼び出せなくなります。

localStorage: バージョン管理 + 最小化

  • 常にversionフィールドを保存し、スキーマ変更時にバージョンを上げ、古いデータを移行または破棄する
  • ペイロードは小さく保つ — localStorageは同期的であり、メインスレッドをブロックする

5. 再レンダリング最適化(中)

コールバック内でのみ使用される状態にサブスクライブしない

// 誤り — countが変更されるたびに再レンダリング
const count = useStore((s) => s.count);
const handler = () => doSomething(count);

// 正しい — 呼び出し時に1回だけ読み取る
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

高コストな処理をメモ化されたコンポーネントに抽出

// 正しい — `items`が変更されたときのみ子が再レンダリング
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

デフォルトの非プリミティブプロップをhoist

// 誤り — レンダリングごとに新しい配列が作成され、memoが機能しない
<List items={items ?? []} />

// 正しい
const EMPTY: Item[] = [];
<List items={items ?? EMPTY} />

エフェクトにはプリミティブな依存配列

// 誤り — レンダリングごとに新しいオブジェクト同一性
useEffect(() => {}, [{ id, name }]);

// 正しい — プリミティブ
useEffect(() => {}, [id, name]);

生の値ではなく、導出されたブール値にサブスクライブ

// 誤り — カートの変更すべてで再レンダリング
const cart = useStore((s) => s.cart);
const hasItems = cart.length > 0;

// 正しい — 空でなくなる/空になる切り替え時のみ再レンダリング
const hasItems = useStore((s) => s.cart.length > 0);

レンダリング中に導出、useEffectは使わない

// 誤り
const [full, setFull] = useState("");
useEffect(() => setFull(`${first} ${last}`), [first, last]);

// 正しい
const full = `${first} ${last}`;

安定したコールバックには関数型setState

// 正しい
const increment = useCallback(() => setCount((c) => c + 1), []);

高コストな値のための遅延状態初期化子

const [tree] = useState(() => parseTree(largeInput));

単純なプリミティブに対するmemoを避ける

useMemo(() => x + 1, [x])はオーバーヘッドです。memoはオブジェクト同一性と高コストな計算にのみ価値があります。

依存配列が独立しているフックは分割

// 誤り — どちらかのソースが変更されると両方のセレクタが再実行
const { a, b } = useSomething(source1, source2);

// 正しい
const a = useA(source1);
const b = useB(source2);

インタラクションロジックをイベントハンドラーに移動

イベントハンドラーはユーザーアクション時にのみ実行されます — useEffectは依存配列が変更されるたびに再実行されます。

緊急でない更新にはstartTransition

const [pending, startTransition] = useTransition();
startTransition(() => setFilters(newFilters));

高コストなレンダリングにはuseDeferredValue

const deferredQuery = useDeferredValue(query);
const results = useMemo(() => expensiveSearch(deferredQuery), [deferredQuery]);

過渡的な頻繁に変更される値にはuseRef

頻繁に変更されるが、再レンダリングをトリガーすべきでない値(タイムスタンプ、最後のキー、アキュムレータ)。

コンポーネントの内部でコンポーネントを定義しない

// 誤り — InnerはOuterがレンダリングされるたびに新しいコンポーネント
function Outer() {
  const Inner = () => <span />;
  return <Inner />;
}

レンダリングのたびに新しいInner型が作成され、Reconciliationが機能せず、子がアンマウントされます。

6. レンダリングパフォーマンス(中)

SVGではなくラッパーをアニメーション化

SVGの周りの<div>ラッパーをtransformすることはGPUアクセラレーションされます。SVG自体をtransformするとペイントがトリガーされます。

長いリストにはcontent-visibility: auto

.row { content-visibility: auto; contain-intrinsic-size: auto 80px; }

ブラウザは画面外のレンダリングをスキップします — 数百行のリストで大きな効果があります。

静的なJSXをhoist

const STATIC_HEADER = <h1>Title</h1>;
function Page() {
  return <>{STATIC_HEADER}<Body /></>;
}

SVG: 座標精度を下げる

d="M10.123456,20.654321" → d="M10.12,20.65"。1桁につきバイト数を消費します。視覚的な違いはサブピクセルです。

インラインスクリプトによるハイドレーション時のちらつき防止

ハイドレーション前に必要な値(テーマ、ロケール)は、Reactがマウントする前にdocument.documentElement.dataset.*を設定する<script>をインライン化します。

予想されるハイドレーションの不一致を狭い範囲で抑制

<time suppressHydrationWarning>{new Date().toLocaleString()}</time>

既知の不一致があるリーフノードにのみ使用 — 他の子を含むツリーには決して使用しないでください。

マウント/アンマウントの代わりに<Activity>で表示/非表示

React 19の<Activity mode="visible|hidden">は、ツリーの状態とエフェクトをマウントしたままにして非表示にします — タブやアコーディオンでのアンマウント/再マウントより安価です。

条件付きレンダリングには&&の代わりに三項演算子

// 誤り — `0`がテキストノードとしてレンダリングされる
{count && <Badge>{count}</Badge>}

// 正しい
{count > 0 ? <Badge>{count}</Badge> : null}

ローディング状態にはuseTransition

アクションと一緒にstartTransitionを使用します。Reactは、次の状態が計算されている間、isPendingとして前のUIを表示します。

React DOMリソースヒント

import { preload, preconnect } from "react-dom";
preload("/api/critical", { as: "fetch" });
preconnect("https://api.example.com");

…

[翻訳の都合上、省略]

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

関連スキル