Code Splitting und Lazy Loading mit Claude Code
React- und Next.js-Code Splitting mit Claude Code sicher umsetzen: Beispiele, Fallstricke, Prüfungen und CTA-Hinweise.
Wenn eine React- oder Next.js-Anwendung langsam wirkt, liegt es oft nicht an einem einzelnen Button, sondern am JavaScript-Bundle. Die Startseite braucht vielleicht nur Text, Navigation und einen CTA, aber der Browser lädt zusätzlich Adminseiten, Diagramme, Editor-Code und Medienfunktionen.
Code Splitting bedeutet, dieses JavaScript in kleinere Chunks zu teilen. Lazy Loading bedeutet, einen Chunk erst zu laden, wenn er wirklich gebraucht wird. Praktisch gesagt: Nicht jeder Besucher muss das Werkzeug für jede spätere Funktion sofort herunterladen.
Dieser Leitfaden zeigt, wie du Claude Code gezielt einsetzt: mit React.lazy, Suspense, dynamischem import(), next/dynamic, Routen- und Seiten-Splitting, Hydration-Prüfung und einem klaren Review. Ergänzend passen Bundle-Analyse, Tree Shaking und Performance-Optimierung.
Begriffe kurz erklärt
Ein bundle ist das JavaScript-Paket, das an den Browser geht. Ein chunk ist eine kleinere Datei nach der Aufteilung. Ein dynamic import wie import("./Chart") lädt ein Modul zur Laufzeit. Suspense ist die React-Grenze, die während des Ladens ein Fallback anzeigt.
hydration bedeutet, dass Browser-JavaScript an bereits vom Server gerendertes HTML Ereignisse und Zustand anhängt. Wenn Server- und Client-Ausgabe unterschiedlich sind, entstehen Hydration-Fehler. Besonders riskant sind frühe Zugriffe auf window, localStorage, Uhrzeit oder Zufallswerte.
| Kandidat | Warum geeignet | Achtung |
|---|---|---|
| Adminbereiche | Für die meisten Besucher irrelevant | Auth- und Ladezustand klar darstellen |
| Diagramme, Karten, Editoren | Häufig große Bibliotheken | Platz reservieren, damit nichts springt |
| Modale und Assistenten | Erst nach Klick nötig | Bei langsamen Klicks vorladen |
| Medien- und Suchwidgets | Oft unterhalb des ersten Bildschirms | Tastatur und Screenreader mitprüfen |
Claude Code sauber beauftragen
Eine gute Aufgabe beschreibt Ziel, Grenzen und Prüfung. So vermeidest du, dass Claude Code wichtige Inhalte hinter Lazy Loading versteckt.
Ziel:
- Schwere UI lazy-loaden, wenn sie nicht für den ersten Screen nötig ist.
- Hero, Artikeltext, Navigation und CTA nicht hinter Lazy Loading verschieben.
- Je nach Framework React.lazy/Suspense oder next/dynamic verwenden.
Zieldateien:
- src/features/reports/ReportsPanel.tsx
- src/features/editor/RichEditor.tsx
- app/admin/page.tsx
Prüfung:
- npm run lint
- npm run build
- Im Network-Tab initiales JS und Lazy-Chunks vergleichen.
- Mobile Ansicht prüfen, damit das Fallback keinen CTA überdeckt.
React.lazy und Suspense
Die React-Dokumentation beschreibt lazy als Möglichkeit, Komponenten-Code erst beim ersten Rendern zu laden. Suspense zeigt währenddessen das Fallback. Wichtig: Lazy-Komponenten auf Modulebene deklarieren, nicht innerhalb einer Komponente.
// src/App.tsx
import { Suspense, lazy, useState } from "react";
const ReportsPanel = lazy(() => import("./ReportsPanel"));
function PanelSkeleton() {
return (
<div role="status" aria-live="polite" style={{ minHeight: 180 }}>
Berichte werden geladen...
</div>
);
}
export default function App() {
const [showReports, setShowReports] = useState(false);
return (
<main>
<h1>Dashboard</h1>
<button type="button" onClick={() => setShowReports(true)}>
Berichte anzeigen
</button>
{showReports ? (
<Suspense fallback={<PanelSkeleton />}>
<ReportsPanel />
</Suspense>
) : (
<p>Die schwere Reporting-UI wird erst bei Bedarf geladen.</p>
)}
</main>
);
}
// src/ReportsPanel.tsx
const rows = [
{ label: "Artikel gelesen", value: "68%" },
{ label: "CTA-Klicks", value: "4.2%" },
{ label: "Beratungsaufrufe", value: "1.1%" },
];
export default function ReportsPanel() {
return (
<section aria-label="Berichte">
<h2>Conversion-Bericht</h2>
<ul>
{rows.map((row) => (
<li key={row.label}>
{row.label}: {row.value}
</li>
))}
</ul>
</section>
);
}
lazy erwartet einen Default Export. Bei Named Exports hilft ein kleiner Wrapper.
// src/lazyNamed.tsx
import { lazy, type ComponentType } from "react";
export function lazyNamed<TModule, TName extends keyof TModule>(
loader: () => Promise<TModule>,
name: TName
) {
return lazy(async () => {
const module = await loader();
return {
default: module[name] as ComponentType,
};
});
}
// src/AnalyticsSlot.tsx
import { Suspense } from "react";
import { lazyNamed } from "./lazyNamed";
const BarChart = lazyNamed(() => import("./charts"), "BarChart");
export function AnalyticsSlot() {
return (
<Suspense fallback={<p>Diagramm wird geladen...</p>}>
<BarChart />
</Suspense>
);
}
Next.js dynamic Import
Next.js trennt Code bereits entlang von Seiten und Routen. next/dynamic ist sinnvoll, wenn eine bestimmte Client Component groß ist oder Browser-APIs braucht. Der import() gehört direkt in den dynamic()-Aufruf und die Deklaration auf Modulebene.
// app/admin/EditorSlot.tsx
"use client";
import dynamic from "next/dynamic";
const RichEditor = dynamic(() => import("./RichEditor"), {
ssr: false,
loading: () => (
<p aria-live="polite" style={{ minHeight: 160 }}>
Editor wird geladen...
</p>
),
});
export default function EditorSlot() {
return <RichEditor initialMarkdown="# Draft" />;
}
// app/admin/page.tsx
import EditorSlot from "./EditorSlot";
export default function AdminPage() {
return (
<main>
<h1>Artikeleditor</h1>
<p>Text und CTA erscheinen sofort, nur der schwere Editor wird verzögert.</p>
<EditorSlot />
</main>
);
}
ssr: false ist nützlich für browserabhängige Editoren, aber schlecht für Artikeltext, Preise, FAQ oder Haupt-CTA. Solche Inhalte sollten serverseitig sichtbar bleiben.
Routen und Seiten zuerst teilen
Beginne mit Routen, bevor du jede kleine Komponente splittest. In Next.js sind getrennte Dateien wie app/reports/page.tsx und app/settings/page.tsx ein natürlicher Schnitt. In React Router kannst du Routenkomponenten lazy laden.
// src/AppRouter.tsx
import { Suspense, lazy } from "react";
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
const HomePage = lazy(() => import("./pages/HomePage"));
const ReportsPage = lazy(() => import("./pages/ReportsPage"));
const SettingsPage = lazy(() => import("./pages/SettingsPage"));
function RouteFallback() {
return <p aria-live="polite">Seite wird geladen...</p>;
}
export default function AppRouter() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/reports">Reports</Link>
<Link to="/settings">Settings</Link>
</nav>
<Suspense fallback={<RouteFallback />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/reports" element={<ReportsPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Praktische Use Cases
Erstens: SaaS-Dashboards. Shell, Navigation und Kernmetriken kommen sofort; Admin-Analysen, Audit Logs, CSV-Export und Diagrammbibliotheken kommen nach Rollenprüfung.
Zweitens: Redaktions- und Kursplattformen. Artikeltext, Lektionstitel und Kauf-CTA bleiben sichtbar. Markdown-Editor, Bildzuschnitt und Vorschau dürfen verzögert laden.
Drittens: Landingpages mit Karten, Videos, Rechnern oder Diagrammen. Die Value Proposition und der nächste Schritt müssen sofort erscheinen; schwere Widgets können nach Scroll oder Klick folgen. Für Medien-UI siehe auch Video-Player und Accessibility.
Viertens: Angebots- oder Checkout-Modale. Der Button ist sofort da, der mehrstufige Dialog lädt erst bei Bedarf. Wenn der erste Klick zu langsam ist, wird beim Sichtbarwerden des Buttons vorgeladen.
Fallstricke
- Zu starkes Splitten erzeugt viele kleine Requests und kann Interaktionen verlangsamen.
lazyoderdynamicinnerhalb einer Komponente kann State zurücksetzen und Preloading verhindern.- Ein Fallback ohne feste Höhe verursacht Layout Shift und verdeckt im schlimmsten Fall CTAs.
- Hydration-Fehler entstehen, wenn Server und Client unterschiedliche Werte rendern.
- SEO- und Umsatzinhalte gehören nicht leichtfertig hinter
ssr: false.
Prüfung
Review-Auftrag an Claude Code:
- Prüfe lazy/dynamic-Deklarationen auf Modulebene.
- Prüfe Default Export gegen Named Export.
- Prüfe, ob Suspense-Grenzen zu groß sind.
- Prüfe, ob ssr:false SEO-Text oder CTA versteckt.
- Suche nach window/date/random/localStorage-Risiken.
- Beschreibe den Vergleich von initialem JS, Lazy-Chunks und Request-Anzahl.
npm run lint
npm run build
Danach im Browser Network auf JS filtern: Beim Reload sollten Editor- oder Admin-Chunks fehlen. Nach Klick oder Navigation sollten sie gezielt erscheinen. Prüfe zusätzlich mobile Breite.
Offizielle Links und CTA
Nutze die offiziellen Quellen: React lazy, React Suspense, Next.js Lazy Loading und Next.js Layouts and Pages.
Für einen wiederholbaren Claude-Code-Ablauf beginne mit dem kostenlosen Cheatsheet. Für wiederverwendbare Review- und Implementierungs-Prompts nutze 50 Claude Code Prompt Templates. Wenn ein Team Regeln, CLAUDE.md und Performance-Reviews gemeinsam aufsetzen will, ist Training und Beratung der passendere nächste Schritt.
Praxisergebnis
In der praktischen Prüfung funktionierte der Ansatz am besten, wenn geschützte Inhalte vorab festgelegt waren: erster Screen, Navigation und CTA blieben servergerendert; Reports und Editor wanderten in Lazy-Chunks. Eine vage Bitte wie „mach es lazy“ führte dagegen zu zu breiten Suspense-Grenzen und unnötigem ssr: false.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Permission Safety Ladder: Zugriff kontrolliert erweitern
Von read-only zu begrenzten Änderungen, Prüfbefehlen und Deploy-Checks mit klarer Kontrolle.
Claude Code Small PR Proof Pack: kleine Änderungen reviewbar machen
Ein Proof Pack für Claude-Code-PRs: Diff, Checks, öffentliche URL, CTA-Pfad und Rollback.
Claude-Code-Review-Gate vor dem Commit
Vor dem Commit mit Claude Code prüfen: Diff, Build, öffentliche URL, Gumroad-Links, Beratung-CTA, fehlende Tests und fremde Dateien.