Communitygithub.com

React-Performance

Optimierungsmuster für React- und Next.js-Performance, adaptiert von Vercel Engineering.

Was ist React-Performance?

Dieses Skill bietet eine strukturierte Sammlung von Leistungsoptimierungsmustern für React 18/19 und Next.js, die auf den bewährten Praktiken von Vercel Labs basieren. Es organisiert die Regeln nach Prioritätsstufen (von kritisch bis niedrig) und bietet eine Entscheidungsmatrix für aktive Code-Reviews und Refactoring. Der Fokus liegt auf der Beseitigung von Wasserfällen (sequenziellen await-Aufrufen), der Optimierung der Bundle-Größe, serverseitiger Performance (React.cache, Server Actions), clientseitiger Datenabfrage (SWR/TanStack Query) und der Vermeidung unnötiger Re-Render. Entwickler erhalten konkrete Code-Beispiele und Anti-Patterns, um Lighthouse Core Web Vitals zu verbessern, Hydration-Probleme zu beheben und die Benutzererfahrung zu optimieren. Das Skill ist für den Einsatz in CI/CD-Pipelines, bei PR-Reviews und während der aktiven Entwicklung gedacht.

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

React Performance

Performance-Optimierungsmuster für React 18/19 und Next.js, adaptiert von Vercel Labs react-best-practices (MIT, v1.0.0). Dieses Skill organisiert Regeln nach Priorität und bietet eine Entscheidungsmatrix für aktive Code-Reviews und Refactoring.

Wann aktivieren

  • Beim Schreiben oder Überprüfen von React/Next.js-Code auf Performance
  • Diagnose langsamer Seitenladezeiten, träger Interaktionen oder hoher CPU-Auslastung auf dem Client
  • Überprüfung der Bundle-Größe oder Regressionen bei Lighthouse Core Web Vitals
  • Eliminierung von Wasserfällen in Server Components / API-Routen
  • Reduzierung clientseitiger Re-Render
  • Optimierung langer Listen, Animationen oder Hydration
  • Überprüfung von Optimierungsentscheidungen in PRs, die app/, pages/, components/ oder Datenebenen betreffen

Prioritätsindex

PrioritätKategoriePräfixWann relevant
1 — KRITISCHWasserfälle eliminierenasync-Immer wenn await von unabhängigem await gefolgt wird
2 — KRITISCHBundle-Größe optimierenbundle-Erstlade-JS, Importe auf Routenebene, Drittanbieter-Bibliotheken
3 — HOCHServerseitige Performanceserver-RSC, Server Actions, API-Routen, SSR
4 — MITTEL-HOCHClientseitige Datenabfrageclient-SWR / TanStack Query / rohes fetch in Hooks
5 — MITTELRe-Render-Optimierungrerender-Hochfrequente Zustandsaktualisierungen, Eltern-Kind-Fan-out
6 — MITTELRendering-Performancerendering-Lange Listen, Animationen, Hydration
7 — NIEDRIG-MITTELJavaScript-Performancejs-Heiße Schleifen, häufige Allokationen
8 — NIEDRIGFortgeschrittene Musteradvanced-Effect-Event-Integration, stabile Referenzen

1. Wasserfälle eliminieren (KRITISCH)

„Wasserfälle sind der Performance-Killer Nr. 1“ — jedes sequenzielle await addiert die vollständige Netzwerklatenz.

Günstige Bedingungen vor await

Prüfe synchrone Bedingungen (Props, Umgebungsvariablen, fest codierte Flags), bevor du entfernte Daten abwartest.

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

// RICHTIG — kurzschließen bei günstiger synchroner Bedingung zuerst
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);
}

Warte ab, bis es benötigt wird

Verschiebe await in den Zweig, der die Daten verwendet.

// FALSCH — wartet ab, bevor entschieden wird, ob die Daten benötigt werden
const user = await getUser(id);
if (mode === "guest") return renderGuest();
return renderUser(user);

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

Promise.all für unabhängige Arbeit

// FALSCH — sequenziell
const user = await getUser(id);
const posts = await getPosts(id);
const followers = await getFollowers(id);

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

Teilabhängigkeiten — früh starten, spät abwarten

// RICHTIG — alle Promises starten, nur abwarten, wenn das jeweilige Ergebnis benötigt wird
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 für Streaming

Setze <Suspense>-Grenzen nah an den Daten, damit die Seite rendern kann, was möglich ist, während langsamere Unterbäume hereingestreamt werden. Der Kompromiss: Layout-Shift bei eintreffendem Inhalt — reserviere Platz (Skeleton oder min-height).

Server Components: Parallelisierung durch Komposition

// FALSCH — Geschwister-await läuft sequenziell innerhalb einer Komponente
export default async function Page() {
  const user = await getUser();
  const cart = await getCart();
  return <View user={user} cart={cart} />;
}

// RICHTIG — in Kinder aufteilen, React führt sie parallel aus
export default async function Page() {
  return (
    <View>
      <UserSection />
      <CartSection />
    </View>
  );
}

2. Bundle-Größe optimieren (KRITISCH)

Direkte Importe, keine Barrels

Barrel-index.ts-Dateien zwingen den Bundler, den gesamten Modulgraph zu durchlaufen, selbst wenn Tree-Shaking den Großteil entfernt. Direkte Importe sparen in vielen realen Anwendungen 200-800 ms der Erstlade-JS.

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

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

Next.js 13.5+ bietet Optimize Package Imports, das dies für gelistete Pakete automatisiert — nutze es; manuelle direkte Importe sind für nicht gelistete Bibliotheken weiterhin erforderlich.

Statisch analysierbare Pfade

// FALSCH — verhindert Bundler-/Trace-Analyse
const mod = await import(`./pages/${name}`);

// RICHTIG — explizit pro Zweig
const mod = name === "home" ? await import("./pages/home") : await import("./pages/about");

Dynamische Importe für schwere Komponenten

import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("./HeavyChart"), {
  loading: () => <Skeleton />,
  ssr: false, // wenn client-only
});

Drittanbieter-Skripte verzögern

Lade Analytics, Logging, Support-Widgets NACH der Hydration. Verwende next/script mit strategy="afterInteractive" (Standard) oder "lazyOnload".

Bedingtes Laden von Modulen

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

Vorladen bei Hover/Fokus

Löse <link rel="preload"> oder import() bei Hover aus, sodass das Bundle im Cache ist, wenn der Benutzer klickt.

3. Serverseitige Performance (HOCH)

Server Actions wie API-Routen authentifizieren

Jede "use server"-Funktion ist ein öffentlicher Endpunkt. Authentifiziere UND autorisiere innerhalb der Action — verlasse dich niemals auf die Absicherung der aufrufenden Client-Komponente.

"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() für anforderungsbezogene Deduplizierung

import { cache } from "react";

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

React.cache dedupliziert innerhalb einer einzelnen Anforderung. Der Aufruf von getUser("1") aus drei Server-Komponenten im selben Render = eine DB-Abfrage.

LRU-Cache für anforderungsübergreifende Daten

Für Daten, die sich NICHT pro Anforderung ändern (Konfiguration, Nachschlagetabellen), cachen außerhalb von React mit einem LRU-Cache oder unstable_cache.

Doppelte Serialisierung in RSC-Props vermeiden

Wenn eine Server-Komponente dieselben Daten in mehrere Client-Komponenten rendert, werden die Daten einmal pro Verbraucher serialisiert. Hebe die Client-Komponente an und übergib Kinder.

Statische I/O auf Modulebene heben

// RICHTIG — wird einmal beim Laden des Moduls ausgeführt
const fontData = readFileSync(fontPath);

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

Kein veränderlicher Modulzustand in RSC/SSR

Modulzustand auf dem Server wird über alle Anforderungen hinweg geteilt — eine Race-Condition zwischen Benutzern. Verwende stattdessen anforderungsbezogenen Speicher (headers(), cookies(), asynchroner Kontext).

Daten, die an Client-Komponenten übergeben werden, minimieren

Serialisiere nur, was der Client benötigt. Entferne Felder, paginiere, projiziere Spalten auf der DB-Ebene.

Verschachtelte Abfragen mit Promise.all pro Element parallelisieren

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

after() für nicht blockierende Arbeit verwenden

Next.js 15 after() führt Arbeit aus, nachdem die Antwort gesendet wurde — 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. Clientseitige Datenabfrage (MITTEL-HOCH)

SWR / TanStack Query für Deduplizierung

Mehrere Komponenten, die useUser(id) aufrufen, sollten eine einzige Netzwerkanfrage und einen Cache-Eintrag gemeinsam nutzen. Verwende SWR oder TanStack Query — implementiere niemals dein eigenes useEffect + fetch für gemeinsam genutzte Daten.

Globale Event-Listener deduplizieren

// FALSCH — jede Komponente fügt ihren eigenen hinzu
useEffect(() => {
  window.addEventListener("scroll", handler);
  return () => window.removeEventListener("scroll", handler);
}, []);

// RICHTIG — einzelner gemeinsam genutzter Listener via Hook + globales Subject
const useScroll = createScrollHook(); // Singleton-Subject unter der Haube

Passive Listener für Scroll

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

Verbessert die Scroll-Flüssigkeit; der Listener kann preventDefault() nicht aufrufen.

localStorage: Version + minimieren

  • Speichere immer ein version-Feld; erhöhe bei Schemaänderung und migriere oder verwerfe alte Daten
  • Halte die Nutzlasten klein — localStorage ist synchron und blockiert den Hauptthread

5. Re-Render-Optimierung (MITTEL)

Kein Zustand abonnieren, der nur in Callbacks verwendet wird

// FALSCH — rendert jedes Mal neu, wenn sich count ändert
const count = useStore((s) => s.count);
const handler = () => doSomething(count);

// RICHTIG — einmal beim Aufruf lesen
const handler = () => {
  const count = useStore.getState().count;
  doSomething(count);
};

Teure Arbeit in memoized Komponenten auslagern

// RICHTIG — Kind rendert nur neu, wenn sich `items` ändert
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

Standardmäßige nicht-primitive Props anheben

// FALSCH — neues Array pro Render unterbricht memo
<List items={items ?? []} />

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

Primitive Abhängigkeiten in Effekten

// FALSCH — neue Objektidentität pro Render
useEffect(() => {}, [{ id, name }]);

// RICHTIG — Primitiven
useEffect(() => {}, [id, name]);

Abgeleitete Booleans abonnieren, nicht Rohwerte

// FALSCH — rendert bei jeder Warenkorbänderung neu
const cart = useStore((s) => s.cart);
const hasItems = cart.length > 0;

// RICHTIG — rendert nur neu, wenn sich der Leerzustand ändert
const hasItems = useStore((s) => s.cart.length > 0);

Während des Renders ableiten, niemals via useEffect

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

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

Funktionale setState für stabile Callbacks

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

Lazy-Zustandsinitialisierer für teure Werte

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

Memo für einfache Primitiven vermeiden

useMemo(() => x + 1, [x]) ist Overhead. Memo lohnt sich bei Objektidentität und teuren Berechnungen.

Hooks mit unabhängigen Abhängigkeiten aufteilen

// FALSCH — beide Selektoren laufen erneut, wenn sich eine Quelle ändert
const { a, b } = useSomething(source1, source2);

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

Interaktionslogik in Event-Handler verschieben

Event-Handler werden nur bei der Benutzeraktion ausgeführt — useEffect wird immer dann erneut ausgeführt, wenn sich Abhängigkeiten ändern.

startTransition für nicht dringende Aktualisierungen

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

useDeferredValue für teure Render

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

useRef für vorübergehende häufige Werte

Für Werte, die sich oft ändern, aber kein erneutes Rendern auslösen sollen (Zeitstempel, letzte Taste, Akkumulatoren).

Keine Komponenten innerhalb von Komponenten definieren

// FALSCH — Inner ist bei jedem Outer-Render eine neue Komponente
function Outer() {
  const Inner = () => <span />;
  return <Inner />;
}

Jeder Render erzeugt einen neuen Inner-Typ, was die Reconciliation untergräbt und Kinder aushängt.

6. Rendering-Performance (MITTEL)

Den Wrapper animieren, nicht das SVG

Das Transformieren eines <div>-Wrappers um ein SVG ist GPU-beschleunigt; das Transformieren des SVGs selbst löst Paint aus.

content-visibility: auto für lange Listen

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

Der Browser überspringt das Rendern außerhalb des Bildschirms — großer Gewinn für Listen mit Hunderten von Zeilen.

Statisches JSX anheben

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

SVG: Koordinatenpräzision reduzieren

d="M10.123456,20.654321" → d="M10.12,20.65". Jede Ziffer kostet Bytes; der visuelle Unterschied ist sub-Pixel.

Hydration ohne Flackern via Inline-Skript

Für Werte, die vor der Hydration benötigt werden (Theme, Gebietsschema), ein <script> inline einfügen, das document.documentElement.dataset.* setzt, bevor React mountet.

Erwartete Hydration-Mismatches eng unterdrücken

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

Nur für bekanntermaßen abweichende Blattknoten verwenden — niemals in einem Baum, der andere Kinder enthält.

<Activity> für Ein-/Ausblenden statt Mount-/Unmount

React 19 <Activity mode="visible|hidden"> behält den Baumzustand und Effekte bei, blendet aber aus — billiger als Unmount/Remount für Tabs und Akkordeons.

Ternärer Operator über && für bedingtes Rendern

// FALSCH — `0` wird als Textknoten gerendert
{count && <Badge>{count}</Badge>}

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

useTransition für Ladezustände

startTransition mit der Aktion paaren; React zeigt die vorherige UI als isPending, während der nächste Zustand berechnet wird.

React DOM-Ressourcen-Hinweise

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

…

[truncated for translation]

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.

Verwandte Skills