Communitygithub.com

react-performance

Patrones de optimización de rendimiento para React y Next.js adaptados de Vercel Engineering.

¿Qué es react-performance?

Este skill proporciona un conjunto completo de patrones y reglas para optimizar el rendimiento en aplicaciones React 18/19 y Next.js, adaptados de las mejores prácticas de Vercel Labs. Organiza las optimizaciones en ocho categorías por prioridad, desde la eliminación de cascadas de peticiones (waterfalls) hasta patrones avanzados, ofreciendo guías de decisión para revisiones de código y refactorización. Es útil para diagnosticar cargas lentas de páginas, altos uso de CPU en el cliente, regresiones en Core Web Vitals, o para mejorar la hidratación y el renderizado de listas largas y animaciones. Funciona con plataformas basadas en React, especialmente Next.js, y se aplica durante la escritura o revisión de código en los directorios `app/`, `pages/`, `components/` y capas de datos.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

React Performance

Patrones de optimización de rendimiento para React 18/19 y Next.js, adaptados de Vercel Labs react-best-practices (MIT, v1.0.0). Este skill organiza las reglas por prioridad y proporciona guías de decisión para la revisión y refactorización activa de código.

Cuándo activarlo

  • Al escribir o revisar código React/Next.js en busca de rendimiento
  • Al diagnosticar cargas lentas de páginas, interacciones lentas o alto uso de CPU en el cliente
  • Al auditar el tamaño del bundle o regresiones en Lighthouse Core Web Vitals
  • Al eliminar cascadas (waterfalls) en Server Components / rutas API
  • Al reducir re-renderizados en el cliente
  • Al optimizar listas largas, animaciones o hidratación
  • Al auditar decisiones de optimización en PRs que toquen app/, pages/, components/ o capas de datos

Índice de prioridad

PrioridadCategoríaPrefijoCuándo importa
1 — CRÍTICOEliminación de cascadasasync-Cada vez que await es seguido de un await independiente
2 — CRÍTICOOptimización del tamaño del bundlebundle-JS de carga inicial, importaciones a nivel de ruta, librerías de terceros
3 — ALTORendimiento del lado del servidorserver-RSC, Server Actions, rutas API, SSR
4 — MEDIO-ALTOObtención de datos en el clienteclient-SWR / TanStack Query / fetch crudo en hooks
5 — MEDIOOptimización de re-renderizadosrerender-Actualizaciones de estado de alta frecuencia, propagación padre-hijo
6 — MEDIORendimiento de renderizadorendering-Listas largas, animaciones, hidratación
7 — BAJO-MEDIORendimiento de JavaScriptjs-Bucles intensivos, asignaciones frecuentes
8 — BAJOPatrones avanzadosadvanced-Integración effect-event, referencias estables

1. Eliminación de cascadas (CRÍTICO)

"Las cascadas son el asesino número uno del rendimiento" — cada await secuencial añade latencia de red completa.

Condiciones baratas antes de await

Verifica condiciones síncronas (props, env, flags hardcodeadas) antes de esperar datos remotos.

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

// CORRECTO — cortocircuitar con condición síncrona barata primero
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);
}

Retrasar awaits hasta ser usados

Mueve await dentro de la rama que lo usa.

// INCORRECTO — espera antes de decidir si necesita los datos
const user = await getUser(id);
if (mode === "guest") return renderGuest();
return renderUser(user);

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

Promise.all para trabajo independiente

// INCORRECTO — secuencial
const user = await getUser(id);
const posts = await getPosts(id);
const followers = await getFollowers(id);

// CORRECTO — paralelo
const [user, posts, followers] = await Promise.all([
  getUser(id),
  getPosts(id),
  getFollowers(id),
]);

Dependencias parciales — empezar temprano, esperar tarde

// CORRECTO — lanzar todas las promesas, esperar solo cuando se necesita cada resultado
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 para streaming

Coloca los límites de <Suspense> cerca de los datos para que la página pinte lo que puede mientras los subárboles más lentos se transmiten. La compensación: cambio de diseño cuando llega el contenido — reserva espacio (skeleton o min-height).

Server Components: paralelismo mediante composición

// INCORRECTO — los awaits hermanos se ejecutan secuencialmente dentro de un componente
export default async function Page() {
  const user = await getUser();
  const cart = await getCart();
  return <View user={user} cart={cart} />;
}

// CORRECTO — dividir en hijos, React los ejecuta en paralelo
export default async function Page() {
  return (
    <View>
      <UserSection />
      <CartSection />
    </View>
  );
}

2. Optimización del tamaño del bundle (CRÍTICO)

Importaciones directas, no barriles

Los archivos barril index.ts fuerzan al bundler a recorrer todo el grafo de módulos aunque tree-shaking elimine la mayor parte. Las importaciones directas ahorran 200-800ms de JS de carga inicial en muchas aplicaciones reales.

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

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

Next.js 13.5+ tiene Optimize Package Imports que automatiza esto para paquetes listados — úsalo; las importaciones directas manuales siguen siendo necesarias para librerías no listadas.

Rutas analizables estáticamente

// INCORRECTO — derrota el análisis del bundler/trace
const mod = await import(`./pages/${name}`);

// CORRECTO — explícito por rama
const mod = name === "home" ? await import("./pages/home") : await import("./pages/about");

Importaciones dinámicas para componentes pesados

import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("./HeavyChart"), {
  loading: () => <Skeleton />,
  ssr: false, // cuando es solo cliente
});

Diferir scripts de terceros

Carga analíticas, logs, widgets de soporte DESPUÉS de la hidratación. Usa next/script con strategy="afterInteractive" (por defecto) o "lazyOnload".

Carga condicional de módulos

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

Precarga al pasar el ratón/enfocar

Activa <link rel="preload"> o import() al pasar el ratón para que el bundle esté en caché cuando el usuario haga clic.

3. Rendimiento del lado del servidor (ALTO)

Autenticar Server Actions como rutas API

Toda función "use server" es un endpoint público. Autentica Y autoriza dentro de la acción — nunca confíes en la compuerta del Client Component que la llama.

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

React.cache() para deduplicación por petición

import { cache } from "react";

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

React.cache deduplica dentro de una sola petición. Llamar a getUser("1") desde tres Server Components en el mismo render = una consulta a la DB.

Caché LRU para datos entre peticiones

Para datos que NO cambian por petición (configuración, tablas de búsqueda), almacena en caché fuera de React con un caché LRU o unstable_cache.

Evitar serialización duplicada en props RSC

Cuando un Server Component renderiza los mismos datos en múltiples Client Components, los datos se serializan una vez por consumidor. Eleva el Client Component y pasa children.

Elevar E/S estática al ámbito del módulo

// CORRECTO — se ejecuta una vez al cargar el módulo
const fontData = readFileSync(fontPath);

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

Sin estado mutable a nivel de módulo en RSC/SSR

El estado del módulo en el servidor se comparte entre todas las peticiones — una condición de carrera entre usuarios. Usa almacenamiento con ámbito de petición (headers(), cookies(), contexto asíncrono) en su lugar.

Minimizar datos pasados a Client Components

Solo serializa lo que el Cliente necesita. Elimina campos, paginación, proyecta columnas en la capa de DB.

Paralelizar búsquedas anidadas con Promise.all por elemento

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

Usar after() para trabajo no bloqueante

Next.js 15 after() ejecuta trabajo después de que se envía la respuesta — registro, calentamiento de caché, analíticas.

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

4. Obtención de datos en el cliente (MEDIO-ALTO)

SWR / TanStack Query para deduplicación

Múltiples componentes llamando a useUser(id) deben compartir una solicitud de red y una entrada de caché. Usa SWR o TanStack Query — nunca implementes tu propio useEffect + fetch para datos compartidos.

Deduplicar listeners de eventos globales

// INCORRECTO — cada componente añade el suyo
useEffect(() => {
  window.addEventListener("scroll", handler);
  return () => window.removeEventListener("scroll", handler);
}, []);

// CORRECTO — un solo listener compartido mediante un hook + sujeto global
const useScroll = createScrollHook(); // sujeto singleton internamente

Listeners pasivos para scroll

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

Mejora la fluidez del scroll; el listener no puede hacer preventDefault().

localStorage: versión + minimizar

  • Siempre almacena un campo version; increméntalo en cambios de esquema y migra o descarta datos antiguos
  • Mantén los payloads pequeños — localStorage es síncrono y bloquea el hilo principal

5. Optimización de re-renderizados (MEDIO)

No te suscribas a estado usado solo en callbacks

// INCORRECTO — re-renderiza cada vez que count cambia
const count = useStore((s) => s.count);
const handler = () => doSomething(count);

// CORRECTO — leer una vez en la llamada
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

Extraer trabajo costoso en componentes memoizados

// CORRECTO — el hijo re-renderiza solo cuando `items` cambia
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

Elevar props no primitivas por defecto

// INCORRECTO — nuevo array en cada render rompe memo
<List items={items ?? []} />

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

Dependencias primitivas en efectos

// INCORRECTO — nueva identidad de objeto en cada render
useEffect(() => {}, [{ id, name }]);

// CORRECTO — primitivos
useEffect(() => {}, [id, name]);

Suscribirse a booleanos derivados, no a valores crudos

// INCORRECTO — re-renderiza por cualquier cambio en cart
const cart = useStore((s) => s.cart);
const hasItems = cart.length > 0;

// CORRECTO — re-renderiza solo cuando cambia el estado vacío/no vacío
const hasItems = useStore((s) => s.cart.length > 0);

Derivar durante el render, nunca mediante useEffect

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

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

setState funcional para callbacks estables

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

Inicializador de estado perezoso para valores costosos

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

Evitar memo para primitivos simples

useMemo(() => x + 1, [x]) es sobrecarga. Memo vale la pena en identidad de objetos y cómputo costoso.

Dividir hooks con dependencias independientes

// INCORRECTO — ambos selectores se re-ejecutan si alguna fuente cambia
const { a, b } = useSomething(source1, source2);

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

Mover lógica de interacción a event handlers

Los event handlers se ejecutan solo en la acción del usuario — useEffect se re-ejecuta cada vez que cambian las dependencias.

startTransition para actualizaciones no urgentes

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

useDeferredValue para renders costosos

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

useRef para valores frecuentes transitorios

Para valores que cambian a menudo pero no deben provocar re-render (timestamps, última tecla, acumuladores).

No definir componentes dentro de componentes

// INCORRECTO — Inner es un nuevo componente en cada render de Outer
function Outer() {
  const Inner = () => <span />;
  return <Inner />;
}

Cada render crea un nuevo tipo Inner, derrotando la reconciliación y desmontando hijos.

6. Rendimiento de renderizado (MEDIO)

Animar el contenedor, no el SVG

Transformar un <div> contenedor alrededor de un SVG está acelerado por GPU; transformar el SVG en sí mismo provoca pintado.

content-visibility: auto para listas largas

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

El navegador salta el renderizado fuera de pantalla — gran mejora para listas con cientos de filas.

Elevar JSX estático

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

SVG: reducir precisión de coordenadas

d="M10.123456,20.654321" → d="M10.12,20.65". Cada dígito cuesta bytes; la diferencia visual es sub-píxel.

Sin parpadeo de hidratación mediante script en línea

Para valores necesarios antes de la hidratación (tema, locale), inserta un <script> que establezca document.documentElement.dataset.* antes de que React monte.

Suprimir discrepancias de hidratación esperadas de forma limitada

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

Usar SOLO para nodos hoja con divergencia conocida — nunca en un árbol que contenga otros hijos.

<Activity> para mostrar/ocultar en lugar de montar/desmontar

React 19 <Activity mode="visible|hidden"> mantiene el estado del árbol y los efectos montados pero oculta — más barato que desmontar/remontar para pestañas y acordeones.

Ternario sobre && para renderizado condicional

// INCORRECTO — `0` se renderiza como nodo de texto
{count && <Badge>{count}</Badge>}

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

useTransition para estados de carga

Combina startTransition con la acción; React muestra la UI anterior como isPending mientras se calcula el siguiente estado.

Pistas de recursos de React DOM

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

...

[truncado para traducción]

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 relacionados