Communitygithub.com

react-performance

Patrons d'optimisation des performances pour React et Next.js, adaptés de Vercel Engineering.

Qu'est-ce que react-performance ?

Ce skill fournit un guide complet pour l'optimisation des performances dans les applications React 18/19 et Next.js, basé sur les bonnes pratiques de Vercel Labs. Il organise les règles par priorité, des plus critiques (élimination des waterfalls, optimisation de la taille du bundle) aux plus avancées (intégration effet-événement, références stables). Pour chaque règle, il propose des exemples de code concrets montrant les anti-patrons et leurs corrections. Le guide couvre aussi bien le côté serveur (Server Components, Server Actions, mise en cache) que le côté client (déduplication des requêtes, optimisation des re-rendus, gestion des listes longues). Il fournit un arbre de décision pour aider à diagnostiquer les problèmes de performance lors des revues de code. Les plateformes cibles incluent React 18/19, Next.js 13.5+, et les bibliothèques de data fetching comme SWR et TanStack Query.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Performances React

Patrons d'optimisation des performances pour React 18/19 et Next.js, adaptés de Vercel Labs react-best-practices (MIT, v1.0.0). Ce skill organise les règles par priorité et fournit un arbre de décision pour la revue de code et le refactoring en cours.

Quand l'activer

  • Écrire ou réviser du code React/Next.js pour les performances
  • Diagnostiquer des chargements de page lents, des interactions lentes ou une utilisation élevée du CPU côté client
  • Auditer la taille du bundle ou les régressions des Core Web Vitals Lighthouse
  • Supprimer les waterfalls dans les Server Components / routes API
  • Réduire les re-rendus côté client
  • Optimiser les listes longues, les animations ou l'hydratation
  • Auditer les choix d'optimisation dans les PR touchant app/, pages/, components/ ou les couches de données

Index de priorité

PrioritéCatégoriePréfixeQuand ça compte
1 — CRITIQUEÉliminer les waterfallsasync-Chaque fois qu'un await est suivi d'un await indépendant
2 — CRITIQUEOptimisation de la taille du bundlebundle-JS de premier chargement, imports par route, bibliothèques tierces
3 — HAUTEPerformances côté serveurserver-RSC, Server Actions, routes API, SSR
4 — MOYENNE-HAUTERécupération de données côté clientclient-SWR / TanStack Query / fetch brut dans les hooks
5 — MOYENNEOptimisation des re-rendusrerender-Mises à jour d'état à haute fréquence, fan-out parent-enfant
6 — MOYENNEPerformances de rendurendering-Listes longues, animations, hydratation
7 — BASSE-MOYENNEPerformances JavaScriptjs-Boucles chaudes, allocations fréquentes
8 — BASSEPatrons avancésadvanced-Intégration effet-événement, références stables

1. Éliminer les waterfalls (CRITIQUE)

"Les waterfalls sont le tueur de performances n°1" — chaque await séquentiel ajoute toute la latence réseau.

Conditions peu coûteuses avant l'await

Vérifiez les conditions synchrones (props, env, flags codés en dur) avant d'attendre des données distantes.

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

// CORRECT — court-circuite d'abord sur une condition synchrone peu coûteuse
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);
}

Reporter les awaits jusqu'à leur utilisation

Déplacez await dans la branche qui l'utilise.

// INCORRECT — attend avant de décider si les données sont nécessaires
const user = await getUser(id);
if (mode === "guest") return renderGuest();
return renderUser(user);

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

Promise.all pour les tâches indépendantes

// INCORRECT — séquentiel
const user = await getUser(id);
const posts = await getPosts(id);
const followers = await getFollowers(id);

// CORRECT — parallèle
const [user, posts, followers] = await Promise.all([
  getUser(id),
  getPosts(id),
  getFollowers(id),
]);

Dépendances partielles — commencez tôt, attendez tard

// CORRECT — lance toutes les promesses, attend chaque résultat seulement quand nécessaire
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 pour le streaming

Placez les limites <Suspense> près des données afin que la page affiche ce qu'elle peut pendant que les sous-arbres plus lents arrivent en streaming. Le compromis : un décalage de mise en page lorsque le contenu arrive — réservez l'espace (skeleton ou min-height).

Server Components : parallélisation par composition

// INCORRECT — les awaits frères s'exécutent séquentiellement dans un seul composant
export default async function Page() {
  const user = await getUser();
  const cart = await getCart();
  return <View user={user} cart={cart} />;
}

// CORRECT — divisez en enfants, React les exécute en parallèle
export default async function Page() {
  return (
    <View>
      <UserSection />
      <CartSection />
    </View>
  );
}

2. Optimisation de la taille du bundle (CRITIQUE)

Imports directs, pas de barils

Les fichiers baril index.ts forcent le bundler à parcourir tout le graphe de modules, même si le tree-shaking supprime la majeure partie. Les imports directs économisent 200 à 800 ms de JS de premier chargement dans de nombreuses applications réelles.

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

// CORRECT
import { Button } from "@/components/Button";
import { Card } from "@/components/Card";
import { Modal } from "@/components/Modal";

Next.js 13.5+ propose Optimize Package Imports qui automatise cela pour les packages listés — utilisez-le ; les imports directs manuels restent nécessaires pour les bibliothèques non listées.

Chemins statiquement analysables

// INCORRECT — empêche l'analyse du bundler/trace
const mod = await import(`./pages/${name}`);

// CORRECT — explicite par branche
const mod = name === "home" ? await import("./pages/home") : await import("./pages/about");

Imports dynamiques pour les composants lourds

import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("./HeavyChart"), {
  loading: () => <Skeleton />,
  ssr: false, // quand côté client uniquement
});

Reporter les scripts tiers

Chargez les outils d'analyse, de journalisation et les widgets d'assistance APRÈS l'hydratation. Utilisez next/script avec strategy="afterInteractive" (par défaut) ou "lazyOnload".

Chargement conditionnel de modules

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

Préchargement au survol/focus

Déclenchez <link rel="preload"> ou import() au survol pour que le bundle soit dans le cache au moment où l'utilisateur clique.

3. Performances côté serveur (HAUTE)

Authentifier les Server Actions comme les routes API

Chaque fonction "use server" est un point de terminaison public. Authentifiez ET autorisez à l'intérieur de l'action — ne vous fiez jamais au garde-fou du Client Component appelant.

"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() pour la déduplication par requête

import { cache } from "react";

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

React.cache déduplique au sein d'une seule requête. Appeler getUser("1") depuis trois Server Components dans le même rendu = une seule requête DB.

Cache LRU pour les données inter-requêtes

Pour les données qui ne changent PAS par requête (configuration, tables de correspondance), mettez en cache en dehors de React avec un cache LRU ou unstable_cache.

Éviter la sérialisation en double dans les props RSC

Lorsqu'un Server Component rend les mêmes données dans plusieurs Client Components, les données sont sérialisées une fois par consommateur. Remontez le Client Component et passez des enfants.

Remonter les E/S statiques au niveau du module

// CORRECT — s'exécute une fois au chargement du module
const fontData = readFileSync(fontPath);

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

Pas d'état mutable au niveau du module dans RSC/SSR

L'état du module sur le serveur est partagé entre toutes les requêtes — une condition de concurrence entre utilisateurs. Utilisez plutôt un stockage à portée de requête (headers(), cookies(), contexte asynchrone).

Minimiser les données transmises aux Client Components

Sérialisez uniquement ce dont le Client a besoin. Supprimez les champs, paginez, projetez les colonnes au niveau de la base de données.

Paralléliser les récupérations imbriquées avec Promise.all par élément

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

Utiliser after() pour les tâches non bloquantes

after() de Next.js 15 exécute le travail après l'envoi de la réponse — journalisation, préchauffage du cache, analyses.

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

4. Récupération de données côté client (MOYENNE-HAUTE)

SWR / TanStack Query pour la déduplication

Plusieurs composants appelant useUser(id) doivent partager une seule requête réseau et une seule entrée de cache. Utilisez SWR ou TanStack Query — ne créez jamais votre propre useEffect + fetch pour des données partagées.

Dédupliquer les écouteurs d'événements globaux

// INCORRECT — chaque composant ajoute le sien
useEffect(() => {
  window.addEventListener("scroll", handler);
  return () => window.removeEventListener("scroll", handler);
}, []);

// CORRECT — un seul écouteur partagé via un hook + un sujet global
const useScroll = createScrollHook(); // singleton sous le capot

Écouteurs passifs pour le défilement

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

Améliore la fluidité du défilement ; l'écouteur ne peut pas appeler preventDefault().

localStorage : version + minimiser

  • Stockez toujours un champ version ; incrémentez-le en cas de changement de schéma et migrez ou supprimez les anciennes données
  • Gardez les charges utiles petites — localStorage est synchrone et bloque le thread principal

5. Optimisation des re-rendus (MOYENNE)

Ne vous abonnez pas à un état utilisé uniquement dans les callbacks

// INCORRECT — re-rend à chaque changement de count
const count = useStore((s) => s.count);
const handler = () => doSomething(count);

// CORRECT — lu une fois lors de l'appel
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

Extrayez le travail coûteux dans des composants mémoïsés

// CORRECT — l'enfant se re-rend uniquement quand `items` change
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

Remontez les props non primitives par défaut

// INCORRECT — nouveau tableau à chaque rendu, ce qui casse memo
<List items={items ?? []} />

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

Dépendances primitives dans les effets

// INCORRECT — nouvel objet à chaque rendu
useEffect(() => {}, [{ id, name }]);

// CORRECT — primitives
useEffect(() => {}, [id, name]);

Abonnez-vous à des booléens dérivés, pas à des valeurs brutes

// INCORRECT — re-rend pour tout changement du panier
const cart = useStore((s) => s.cart);
const hasItems = cart.length > 0;

// CORRECT — re-rend uniquement quand l'état vide/vide change
const hasItems = useStore((s) => s.cart.length > 0);

Dérivez pendant le rendu, jamais via useEffect

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

// CORRECT
const full = `${first} ${last}`;

setState fonctionnel pour les callbacks stables

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

Initialisateur d'état paresseux pour les valeurs coûteuses

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

Évitez memo pour les primitives simples

useMemo(() => x + 1, [x]) est une surcharge. Memo est utile pour l'identité d'objet et les calculs coûteux.

Séparez les hooks avec des dépendances indépendantes

// INCORRECT — les deux sélecteurs se réexécutent si l'une ou l'autre source change
const { a, b } = useSomething(source1, source2);

// CORRECT
const a = useA(source1);
const b = useB(source2);

Déplacez la logique d'interaction dans les gestionnaires d'événements

Les gestionnaires d'événements s'exécutent uniquement sur l'action de l'utilisateur — useEffect se réexécute chaque fois que les dépendances changent.

startTransition pour les mises à jour non urgentes

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

useDeferredValue pour les rendus coûteux

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

useRef pour les valeurs fréquentes transitoires

Pour les valeurs qui changent souvent mais ne doivent pas déclencher de re-rendu (horodatages, dernière touche, accumulateurs).

Ne définissez pas de composants à l'intérieur des composants

// INCORRECT — Inner est un nouveau composant à chaque rendu d'Outer
function Outer() {
  const Inner = () => <span />;
  return <Inner />;
}

Chaque rendu crée un nouveau type Inner, ce qui empêche la réconciliation et démonte les enfants.

6. Performances de rendu (MOYENNE)

Animez le wrapper, pas le SVG

Transformer un <div> wrapper autour d'un SVG est accéléré par le GPU ; transformer le SVG lui-même déclenche un repaint.

content-visibility: auto pour les listes longues

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

Le navigateur saute le rendu hors écran — un gain majeur pour les listes de centaines de lignes.

Remontez le JSX statique

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

SVG : réduisez la précision des coordonnées

d="M10.123456,20.654321" → d="M10.12,20.65". Chaque chiffre coûte des octets ; la différence visuelle est inférieure au pixel.

Pas de scintillement d'hydratation via un script en ligne

Pour les valeurs nécessaires avant l'hydratation (thème, locale), intégrez un <script> qui définit document.documentElement.dataset.* avant que React ne monte.

Supprimez les avertissements d'hydratation attendus de manière ciblée

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

À utiliser UNIQUEMENT pour les nœuds feuilles connus pour diverger — jamais sur un arbre contenant d'autres enfants.

<Activity> pour afficher/masquer au lieu de monter/démonter

React 19 <Activity mode="visible|hidden"> maintient l'état de l'arbre et les effets montés mais les masque — moins coûteux que démonter/remonter pour les onglets et les accordéons.

Opérateur ternaire plutôt que && pour le rendu conditionnel

// INCORRECT — `0` est rendu comme nœud de texte
{count && <Badge>{count}</Badge>}

// CORRECT
{count > 0 ? <Badge>{count}</Badge> : null}

useTransition pour les états de chargement

Associez startTransition à l'action ; React affiche l'UI précédente comme isPending pendant que l'état suivant se calcule.

Indices de ressources 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.

Skills associés