Communitygithub.com

desempenho-react

Padrões de otimização de desempenho para React e Next.js adaptados da Vercel Engineering

O que é desempenho-react?

Esta skill fornece um conjunto abrangente de padrões de otimização de desempenho para aplicações React 18/19 e Next.js, organizados por prioridade e com orientação prática para revisão de código e refatoração. Baseada nas melhores práticas da Vercel Labs, ela cobre desde a eliminação de waterfalls em chamadas assíncronas (considerada a principal causa de perda de desempenho) até otimizações de bundle, desempenho no servidor, busca de dados no cliente e redução de re-renderizações. A skill é ideal para desenvolvedores que estão diagnosticando problemas como carregamento lento de páginas, más pontuações no Lighthouse/Core Web Vitals, ou que precisam auditar PRs que envolvem componentes React, camadas de dados ou roteamento. Ela funciona como um guia de decisão durante revisões de código ativas, oferecendo exemplos práticos de código incorreto vs. correto para cada cenário, com suporte explícito para Next.js 13.5+, React 19 e padrões como Server Components, Server Actions e Suspense.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Desempenho React

Padrões de otimização de desempenho para React 18/19 e Next.js, adaptados de Vercel Labs react-best-practices (MIT, v1.0.0). Esta skill organiza regras por prioridade e fornece orientação baseada em árvore de decisão para revisão de código ativa e refatoração.

Quando Ativar

  • Escrevendo ou revisando código React/Next.js para desempenho
  • Diagnosticando carregamentos lentos de página, interações lentas ou alto uso de CPU no cliente
  • Auditando tamanho do bundle ou regressões no Lighthouse/Core Web Vitals
  • Removendo waterfalls em Server Components / rotas de API
  • Reduzindo re-renderizações no lado do cliente
  • Otimizando listas longas, animações ou hidratação
  • Auditando escolhas de otimização em PRs que envolvem app/, pages/, components/ ou camadas de dados

Índice de Prioridade

PrioridadeCategoriaPrefixoQuando importa
1 — CRÍTICAEliminando Waterfallsasync-Sempre que await é seguido por await independente
2 — CRÍTICAOtimização de Tamanho do Bundlebundle-JS de carregamento inicial, importações por rota, bibliotecas de terceiros
3 — ALTADesempenho no Lado do Servidorserver-RSC, Server Actions, rotas de API, SSR
4 — MÉDIA-ALTABusca de Dados no Clienteclient-SWR / TanStack Query / fetch puro em hooks
5 — MÉDIAOtimização de Re-renderizaçãorerender-Atualizações de estado de alta frequência, propagação pai-filho
6 — MÉDIADesempenho de Renderizaçãorendering-Listas longas, animações, hidratação
7 — BAIXA-MÉDIADesempenho JavaScriptjs-Loops intensos, alocações frequentes
8 — BAIXAPadrões Avançadosadvanced-Integração efeito-evento, referências estáveis

1. Eliminando Waterfalls (CRÍTICA)

"Waterfalls são o maior assassino de desempenho" — cada await sequencial adiciona latência de rede completa.

Condições baratas antes de await

Verifique condições síncronas (props, env, flags fixas) antes de aguardar dados remotos.

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

// CORRETO — curto-circuito na condição síncrona barata primeiro
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);
}

Adie awaits até o uso

Mova await para dentro do ramo que o utiliza.

// INCORRETO — aguarda antes de decidir se precisa dos dados
const user = await getUser(id);
if (mode === "guest") return renderGuest();
return renderUser(user);

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

Promise.all para trabalho independente

// INCORRETO — sequencial
const user = await getUser(id);
const posts = await getPosts(id);
const followers = await getFollowers(id);

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

Dependências parciais — inicie cedo, aguarde tarde

// CORRETO — inicie todas as promises, aguarde cada resultado apenas quando necessário
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

Coloque limites de <Suspense> próximos dos dados para que a página renderize o que puder enquanto subárvores mais lentas chegam via streaming. A compensação: mudança de layout quando o conteúdo chega — reserve espaço (skeleton ou min-height).

Server Components: paralelo através de composição

// INCORRETO — awaits irmãos executam sequencialmente dentro de um componente
export default async function Page() {
  const user = await getUser();
  const cart = await getCart();
  return <View user={user} cart={cart} />;
}

// CORRETO — divida em filhos, React os executa em paralelo
export default async function Page() {
  return (
    <View>
      <UserSection />
      <CartSection />
    </View>
  );
}

2. Otimização de Tamanho do Bundle (CRÍTICA)

Importações diretas, não barris

Arquivos index.ts de barris forçam o bundler a percorrer todo o grafo de módulos, mesmo que o tree-shaking remova a maior parte. Importações diretas economizam 200-800ms de JS de carregamento inicial em muitos aplicativos reais.

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

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

Next.js 13.5+ tem Optimize Package Imports que automatiza isso para pacotes listados — use-o; importações diretas manuais ainda são necessárias para libs não listadas.

Caminhos estaticamente analisáveis

// INCORRETO — impede análise do bundler/trace
const mod = await import(`./pages/${name}`);

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

Importações dinâmicas para componentes pesados

import dynamic from "next/dynamic";

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

Adie scripts de terceiros

Carregue análises, logging, widgets de suporte APÓS a hidratação. Use next/script com strategy="afterInteractive" (padrão) ou "lazyOnload".

Carregamento condicional de módulos

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

Pré-carregue ao passar o mouse/foco

Dispare <link rel="preload"> ou import() ao passar o mouse para que o bundle esteja em cache quando o usuário clicar.

3. Desempenho no Lado do Servidor (ALTA)

Autentique Server Actions como rotas de API

Toda função "use server" é um endpoint público. Autentique E autorize dentro da ação — nunca confie na proteção do Client Component chamador.

"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 desduplicação por requisição

import { cache } from "react";

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

React.cache desduplica dentro de uma única requisição. Chamar getUser("1") de três Server Components na mesma renderização = uma consulta ao DB.

Cache LRU para dados entre requisições

Para dados que NÃO mudam por requisição (config, tabelas de consulta), faça cache fora do React com um cache LRU ou unstable_cache.

Evite serialização duplicada em props RSC

Quando um Server Component renderiza os mesmos dados em vários Client Components, os dados são serializados uma vez por consumidor. Eleve o Client Component e passe children.

Eleve E/S estática ao escopo do módulo

// CORRETO — executa uma vez no carregamento do módulo
const fontData = readFileSync(fontPath);

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

Sem estado mutável no nível do módulo em RSC/SSR

Estado de módulo no servidor é compartilhado entre todas as requisições — uma condição de corrida entre usuários. Use armazenamento com escopo de requisição (headers(), cookies(), contexto assíncrono) como alternativa.

Minimize dados passados para Client Components

Serializa apenas o que o Client precisa. Remova campos, pagine, projete colunas na camada do DB.

Paralelize buscas aninhadas com Promise.all por item

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

Use after() para trabalho não bloqueante

after() do Next.js 15 executa trabalho após o envio da resposta — logging, cache warming, analytics.

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

4. Busca de Dados no Cliente (MÉDIA-ALTA)

SWR / TanStack Query para desduplicação

Múltiplos componentes chamando useUser(id) devem compartilhar uma requisição de rede e uma entrada de cache. Use SWR ou TanStack Query — nunca crie seu próprio useEffect + fetch para dados compartilhados.

Desduplicar listeners de eventos globais

// INCORRETO — cada componente adiciona o seu
useEffect(() => {
  window.addEventListener("scroll", handler);
  return () => window.removeEventListener("scroll", handler);
}, []);

// CORRETO — único listener compartilhado via hook + subject global
const useScroll = createScrollHook(); // subject singleton por baixo dos panos

Listeners passivos para scroll

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

Melhora a suavidade da rolagem; o listener não pode chamar preventDefault().

localStorage: versão + minimização

  • Sempre armazene um campo version; incremente na mudança de schema e migre ou descarte dados antigos
  • Mantenha payloads pequenos — localStorage é síncrono e bloqueia a thread principal

5. Otimização de Re-renderização (MÉDIA)

Não se inscreva em estado usado apenas em callbacks

// INCORRETO — re-renderiza toda vez que count muda
const count = useStore((s) => s.count);
const handler = () => doSomething(count);

// CORRETO — lê uma vez na chamada
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

Extraia trabalho pesado para componentes memoizados

// CORRETO — filho re-renderiza apenas quando `items` muda
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

Eleve props não primitivas padrão

// INCORRETO — novo array a cada renderização quebra memo
<List items={items ?? []} />

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

Dependências primitivas em effects

// INCORRETO — nova identidade de objeto a cada renderização
useEffect(() => {}, [{ id, name }]);

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

Inscreva-se em booleanos derivados, não em valores brutos

// INCORRETO — re-renderiza para qualquer mudança no carrinho
const cart = useStore((s) => s.cart);
const hasItems = cart.length > 0;

// CORRETO — re-renderiza apenas quando o estado de vazio muda
const hasItems = useStore((s) => s.cart.length > 0);

Derive durante a renderização, nunca via useEffect

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

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

setState funcional para callbacks estáveis

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

Inicializador de estado lazy para valores caros

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

Evite memo para primitivas simples

useMemo(() => x + 1, [x]) é overhead. Memo vale a pena para identidade de objeto e computação cara.

Divida hooks com dependências independentes

// INCORRETO — ambos os seletores reexecutam se qualquer fonte mudar
const { a, b } = useSomething(source1, source2);

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

Mova lógica de interação para event handlers

Event handlers executam apenas na ação do usuário — useEffect reexecuta sempre que as dependências mudam.

startTransition para atualizações não urgentes

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

useDeferredValue para renderizações caras

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

useRef para valores transitórios frequentes

Para valores que mudam com frequência mas não devem disparar re-renderização (timestamps, última tecla, acumuladores).

Não defina componentes dentro de componentes

// INCORRETO — Inner é um novo componente em cada renderização de Outer
function Outer() {
  const Inner = () => <span />;
  return <Inner />;
}

Cada renderização cria um novo tipo Inner, derrotando a reconciliação e desmontando filhos.

6. Desempenho de Renderização (MÉDIA)

Anime o wrapper, não o SVG

Transformar um wrapper <div> em torno de um SVG é acelerado por GPU; transformar o SVG em si dispara pintura.

content-visibility: auto para listas longas

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

O navegador pula a renderização fora da tela — grande ganho para listas com centenas de linhas.

Eleve JSX estático

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

SVG: reduza a precisão de coordenadas

d="M10.123456,20.654321" → d="M10.12,20.65". Cada dígito custa bytes; a diferença visual é sub-pixel.

Hidratação sem flicker via script inline

Para valores necessários antes da hidratação (tema, locale), coloque inline um <script> que define document.documentElement.dataset.* antes de o React montar.

Suprima warnings de hidratação esperados de forma restrita

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

Use APENAS para nós folha sabidamente divergentes — nunca em uma árvore contendo outros filhos.

<Activity> para mostrar/esconder em vez de montar/desmontar

React 19 <Activity mode="visible|hidden"> mantém o estado da árvore e effects montados mas oculta — mais barato que desmontar/remontar para abas e accordions.

Ternário em vez de && para renderização condicional

// INCORRETO — `0` renderiza como nó de texto
{count && <Badge>{count}</Badge>}

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

useTransition para estados de carregamento

Combine startTransition com a ação; React mostra a UI anterior como isPending enquanto o próximo estado é calculado.

Dicas de recurso 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.

Habilidades Relacionadas