Communitygithub.com

React Performance

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

Was ist React Performance?

Diese Skill-Anleitung bietet eine strukturierte, nach Prioritäten geordnete Sammlung von Performance-Optimierungsmustern für React 18/19 und Next.js, basierend auf den `react-best-practices` von Vercel Labs. Sie ist für Entwickler konzipiert, die aktiv Code überprüfen oder refaktorisieren, um Ladezeiten, Interaktionen und Bundle-Größen zu verbessern. Die Anleitung deckt acht Prioritätsstufen ab, von kritischen Wasserfällen (Waterfalls) und Bundle-Optimierung bis hin zu fortgeschrittenen Mustern wie `React.cache()` und `startTransition`. Jeder Abschnitt enthält klare, bewährte Code-Beispiele und Erklärungen, die helfen, häufige Performance-Fehler zu vermeiden. Die Skill eignet sich besonders für Code-Reviews in `app/`, `pages/`, `components/` und Datenabfrageschichten und unterstützt Entwickler dabei, Core Web Vitals zu verbessern und clientseitige Neu-Renderings zu reduzieren.

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

In Ihrer bevorzugten KI fragen

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

Dokumentation

Was macht React Performance?

Optimierungsmuster für React 18/19 und Next.js, abgeleitet von Vercel Labs react-best-practices (MIT, v1.0.0). Diese Skill organisiert Regeln nach Priorität und bietet Entscheidungsbäume für aktive Code-Reviews und Refaktorisierungen.

Wann aktivieren

  • Beim Schreiben oder Überprüfen von React/Next.js-Code auf Performance
  • Bei der Diagnose von langsamen Seitenladezeiten, trägen Interaktionen oder hoher CPU-Auslastung auf dem Client
  • Bei der Überprüfung der Bundle-Größe oder von Regressionen bei Lighthouse Core Web Vitals
  • Beim Entfernen von Waterfalls in Server Components / API-Routen
  • Beim Reduzieren clientseitiger Neu-Renderings
  • Bei der Optimierung von langen Listen, Animationen oder Hydratation
  • Bei der Überprüfung von Optimierungsentscheidungen in PRs, die app/, pages/, components/ oder Datenabfrageschichten betreffen

Prioritätsindex

PrioritätKategoriePräfixWann es wichtig ist
1 — KRITISCHWaterfalls beseitigenasync-Immer wenn await auf unabhängiges await folgt
2 — KRITISCHBundle-Größenoptimierungbundle-Erstladungs-JS, Imports auf Routenebene, Drittanbieter-Bibliotheken
3 — HOCHServerseitige Performanceserver-RSC, Server Actions, API-Routen, SSR
4 — MITTEL-HOCHClientseitiges Datenabrufenclient-SWR / TanStack Query / rohes fetch in Hooks
5 — MITTELNeu-Renderungsoptimierungrerender-Häufige Statusaktualisierungen, Eltern-Kind-Fan-Out
6 — MITTELRendering Performancerendering-Lange Listen, Animationen, Hydratation
7 — NIEDRIG-MITTELJavaScript Performancejs-Heiße Schleifen, häufige Allokationen
8 — NIEDRIGFortgeschrittene Musteradvanced-Effect-Event-Integration, stabile Refs

1. Waterfalls beseitigen (KRITISCH)

„Waterfalls sind der Performance-Killer Nummer eins" — jedes sequenzielle await fügt vollständige Netzwerklatenz hinzu.

Günstige Bedingungen vor await

Überprüfe synchrone Bedingungen (Props, Umgebung, hartcodierte Flags), bevor entfernte Daten abgerufen werden.

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

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

await bis zur Verwendung verschieben

Verschiebe await in den Zweig, der es verwendet.

// FALSCH — await vor der Entscheidung, 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 Arbeiten

// 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),
]);

Partielle Abhängigkeiten — früh starten, spät awaiten

// RICHTIG — alle Promises starten, nur awaiten, wenn jedes 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, sodass die Seite das zeichnen kann, was möglich ist, während langsamere Unterbäume nachgeladen werden. Der Kompromiss: Layout-Shift, wenn Inhalte eintreffen — reserviere Platz (Skeleton oder min-height).

Server Components: Parallelisierung durch Komposition

// FALSCH — Geschwister-await laufen 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ößenoptimierung (KRITISCH)

Direkte Imports, keine Fassadendateien (Barrel)

Fassaden-index.ts-Dateien zwingen den Bundler, den gesamten Modul-Graphen zu durchlaufen, selbst wenn Tree-Shaking den Großteil entfernt. Direkte Imports sparen in vielen realen Anwendungen 200-800ms Erstladungs-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 aufgeführte Pakete automatisiert — nutze es; manuelle direkte Imports sind dennoch für nicht aufgeführte Bibliotheken erforderlich.

Statisch analysierbare Pfade

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

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

Dynamische Imports für schwere Komponenten

import dynamic from "next/dynamic";

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

Drittanbieter-Skripte verschieben

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

Bedingtes Modulladen

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

Vorausladen 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 Aktion — 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. Drei Aufrufe von getUser("1") aus verschiedenen Server Components im selben Render = eine DB-Abfrage.

LRU-Cache für anforderungsübergreifende Daten

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

Doppelte Serialisierung in RSC-Props vermeiden

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

Statische I/O auf Modulebene anheben

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

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

Kein veränderlicher Modulstatus auf Modulebene in RSC/SSR

Modulstatus auf dem Server wird von allen Anforderungen gemeinsam genutzt — eine Race Condition zwischen Benutzern. Verwende stattdessen anforderungsspezifischen Speicher (headers(), cookies(), asynchroner Kontext).

An Client Components übergebene Daten minimieren

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

Verschachtelte Abrufe 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 Arbeiten verwenden

Next.js 15 after() führt Arbeiten aus, nachdem die Antwort gesendet wurde — Logging, Cache-Warming, Analytik.

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

4. Clientseitiges Datenabrufen (MITTEL-HOCH)

SWR / TanStack Query für Deduplizierung

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

Globale Ereignis-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 über einen 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-Glätte; der Listener kann kein preventDefault() aufrufen.

localStorage: Version + minimieren

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

5. Neu-Renderungsoptimierung (MITTEL)

Kein Abonnieren von Status, der nur in Callbacks verwendet wird

// FALSCH — rendert jedes Mal neu, wenn count sich ä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 `items` sich ändert
const Heavy = memo(function Heavy({ items }: { items: Item[] }) {
  return <Chart data={transform(items)} />;
});

Standardmäßig nicht primitive Props anheben

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

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

Primitive Abhängigkeiten in Effekten

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

// RICHTIG — Primitive
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 der Leerzustand umkippt
const hasItems = useStore((s) => s.cart.length > 0);

Während des Render ableiten, niemals via useEffect

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

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

Funktionelles setState für stabile Callbacks

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

Lazy State Initializer für teure Werte

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

Memo für einfache Primitive vermeiden

useMemo(() => x + 1, [x]) ist Overhead. Memo lohnt sich bei Objekt-Identitä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 läuft erneut, wenn sich Abhängigkeiten ändern.

startTransition für nicht dringende Aktualisierungen

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

useDeferredValue für teure Renderings

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

useRef für flüchtige, häufige Werte

Für Werte, die sich oft ändern, aber kein Neu-Rendering 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-Typen, was die Reconciliation verhindert und Kinder aushängt.

6. Rendering Performance (MITTEL)

Das Wrapper-Element animieren, nicht das SVG

Die Transformation eines <div>-Wrappers um ein SVG ist GPU-beschleunigt; die Transformation 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 Rendering außerhalb des Bildschirms — großer Gewinn für Listen mit Hunderten von Zeilen.

Statisches JSX anheben

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

SVG: Koordinaten-Präzision reduzieren

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

Kein Hydration-Flimmern via Inline-Script

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

Erwartete Hydration-Mismatches eng begrenzt unterdrücken

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

Nur für bekannte, divergierende Blattknoten verwenden — niemals für einen Baum, der andere Kinder enthält.

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

React 19 <Activity mode="visible|hidden"> hält den Baumstatus und Effekte gemountet, blendet aber aus — günstiger als Unmount/Remount für Tabs und Akkordeons.

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

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

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

useTransition für Ladezustände

Paare startTransition mit der Aktion; 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")

…

(gekürzt für die Übersetzung)

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.

Verwandte Skills