Communitygithub.com

rendimiento-react

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

¿Qué es rendimiento-react?

Este skill ofrece una guía completa de optimización de rendimiento para aplicaciones React 18/19 y Next.js, basada en las prácticas recomendadas por Vercel Engineering. Organiza un conjunto de reglas priorizadas (desde críticas hasta avanzadas) con un índice de prioridad que facilita la toma de decisiones durante la revisión de código o la refactorización. Cubre desde la eliminación de 'waterfalls' (cascadas de peticiones secuenciales) y la optimización del tamaño del bundle, hasta el rendimiento del servidor, la obtención de datos en cliente y la reducción de re-renderizados. Incluye ejemplos de código detallados para cada patrón, mostrando las prácticas incorrectas y correctas, y explica cuándo activar cada regla en función del problema de rendimiento diagnosticado (carga lenta de página, alto uso de CPU, problemas de Core Web Vitals, etc.). Es útil tanto para desarrolladores que escriben nuevo código como para aquellos que auditan PRs existentes.

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

Preguntar en tu IA favorita

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

Documentación

Rendimiento en React

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 un árbol de decisiones para la revisión y refactorización activa de código.

Cuándo activarlo

  • Al escribir o revisar código React/Next.js para rendimiento
  • Al diagnosticar cargas lentas de página, interacciones lentas o alto uso de CPU en el cliente
  • Al auditar el tamaño del bundle o regresiones de Core Web Vitals de Lighthouse
  • Al eliminar 'waterfalls' (cascadas) en Server Components / rutas API
  • Al reducir re-renderizados en el cliente
  • Al optimizar listas largas, animaciones o hidratación
  • Al auditar elecciones 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 Waterfallsasync-Cada vez que await es seguido por 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 nativo 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 efecto-evento, referencias estables

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

"Los 'waterfalls' (cascadas) son el asesino número uno del rendimiento" — cada await secuencial agrega latencia de red completa.

Condiciones baratas antes de await

Verifica condiciones síncronas (props, env, flags fijas en código) 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 — cortocircuito en 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);
}

Diferir awaits hasta que se usen

Mueve await a la rama que lo utiliza.

// 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 — iniciar temprano, esperar tarde

// CORRECTO — lanza todas las promesas, espera 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 límites de <Suspense> cerca de los datos para que la página pinte lo que pueda mientras los sub-árboles más lentos se transmiten. La compensación: cambio de diseño ((layout shift)) cuando llega el contenido — reserva espacio (esqueleto o min-height).

Server Components: paralelizar 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 de barril index.ts obligan al empaquetador ((bundler)) a recorrer todo el grafo de módulos incluso cuando el tree-shaking ((eliminación de código muerto)) elimina la mayor parte. Las importaciones directas ahorran 200-800 ms 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 (usa esta configuración); las importaciones directas manuales aún son necesarias para librerías no listadas.

Rutas analizables estáticamente

// INCORRECTO — anula el análisis del empaquetador/rastreo
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 solo es para cliente
});

Diferir scripts de terceros

Carga scripts de analítica, logging y 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");
  // ...
}

Precargar al pasar el ratón/enfocar

Dispara <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

Cada función "use server" es un endpoint público. Autentica Y autoriza dentro de la acción — nunca confíes en la protección del Componente Cliente que la llama.

"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() para deduplicación por solicitud

import { cache } from "react";

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

React.cache deduplica dentro de una misma solicitud. Llamar a getUser("1") desde tres Server Components en el mismo renderizado = una consulta a la base de datos.

Caché LRU para datos entre solicitudes

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

Evitar serialización duplicada en props de 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 solicitudes — una condición de carrera entre usuarios. Usa almacenamiento con ámbito de solicitud (headers(), cookies(), contexto asíncrono) en su lugar.

Minimizar datos pasados a Client Components

Serializa solo lo que el Cliente necesita. Elimina campos, pagina, proyecta columnas a nivel de base de datos.

Paralelizar fetchs anidados 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

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

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 que llaman a useUser(id) deberían 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 único listener compartido mediante un hook + sujeto global
const useScroll = createScrollHook(); // sujeto singleton bajo el capó

Listeners pasivos para scroll

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

Mejora la suavidad del desplazamiento; el listener no puede llamar a preventDefault().

localStorage: versión + minimizar

  • Siempre almacena un campo version; aumenta 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 — lee una vez al llamar
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

Extrae 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)} />;
});

Eleva props no primitivas por defecto

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

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

Dependencias primitivas en efectos

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

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

Suscríbete a booleanos derivados, no a valores brutos

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

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

Deriva durante el renderizado, 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));

Evita memo para primitivas simples

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

Divide hooks con dependencias independientes

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

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

Mueve la lógica de interacción a manejadores de eventos

Los manejadores de eventos se ejecutan solo con la acción del usuario — useEffect se re-ejecuta cada vez que las dependencias cambian.

startTransition para actualizaciones no urgentes

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

useDeferredValue para renderizados costosos

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

useRef para valores transitorios frecuentes

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

No definas componentes dentro de componentes

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

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

6. Rendimiento de Renderizado (MEDIO)

Anima el contenedor, no el SVG

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

content-visibility: auto para listas largas

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

El navegador omite el renderizado fuera de pantalla — gran beneficio para listas con cientos de filas.

Eleva JSX estático

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

SVG: reduce la precisión de coordenadas

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

Sin parpadeo de hidratación mediante script inline

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

Suprime discrepancias de hidratación esperadas de forma estrecha

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

Usa SOLO en nodos hoja conocidos por divergir — nunca en un árbol que contenga otros hijos.

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

<Activity mode="visible|hidden"> de React 19 mantiene el estado del árbol y los efectos montados pero los oculta — más barato que desmontar/volver a montar 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 el siguiente estado se computa.

Pistas de recursos de 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/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

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.

Skills relacionados