Bundle Analyse und Code Splitting Audits für React
AI generated
</>
{ }
React · Bundle Analyse · Code Splitting · Performance
Bundle Analyse und Code Splitting Audits
React Apps dauerhaft schlank halten

Bundle Analyse in React Apps ist kein einmaliges Optimierungsprojekt, sondern ein wiederkehrender Audit-Prozess. Ohne Visualizer-Tools, Größen-Budgets in der CI und regelmäßige Prüfung auf doppelte Abhängigkeiten wächst jedes Bundle unbemerkt, bis Ladezeiten und Core Web Vitals spürbar leiden.

18 Min. Lesezeit Vite Visualizer · Source Map Explorer CI-Budgets · Dynamic Import

1. Warum Bundle-Größe ein Audit-Prozess ist, kein einmaliges Thema

Viele Teams betreiben Bundle Analyse genau einmal, kurz vor einem großen Release, optimieren die größten Auffälligkeiten und vergessen das Thema danach für Monate. Das Problem: jede neue Abhängigkeit, jede neue Komponente und jeder importierte Icon-Satz trägt ein wenig zur Bundle-Größe bei, und diese kleinen Zuwächse summieren sich über ein Jahr zu einem deutlich langsameren Ladeverhalten, ohne dass ein einzelner Commit dafür verantwortlich gemacht werden könnte.

Bundle Analyse muss deshalb als kontinuierlicher Audit-Prozess verstanden werden, nicht als abgeschlossenes Projekt. Ein einmaliges Aufräumen bringt kurzfristig Verbesserung, verhindert aber nicht, dass sich derselbe Zustand innerhalb weniger Monate wiederholt, wenn keine strukturellen Kontrollen wie CI-Budgets etabliert werden. Die folgenden Abschnitte zeigen konkrete Tools und Prozesse für eine wiederkehrende Bundle Analyse, die Regressionen verhindert statt sie nachträglich zu reparieren.

Der Kern eines guten Audit-Prozesses für Code Splitting und Bundle-Größe besteht aus drei Bausteinen: Sichtbarkeit durch Visualizer-Tools, harte Grenzen durch CI-Budgets, und systematisches Vorgehen bei doppelten Abhängigkeiten. Alle drei werden im Folgenden mit konkreten Konfigurationen gezeigt.

2. Bundle Analyse mit vite-plugin-visualizer und Source Map Explorer

Der erste Schritt jeder Bundle Analyse ist Sichtbarkeit: welches Modul trägt wie viel zur finalen Bundle-Größe bei. Für Vite-basierte React-Projekte liefert rollup-plugin-visualizer eine interaktive Treemap-Darstellung, die nach jedem Build als HTML-Datei erzeugt wird und exakt zeigt, welche Abhängigkeit wie viele Kilobyte im finalen Bundle belegt.


// vite.config.ts — generate a bundle visualization after every build
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  plugins: [
    react(),
    visualizer({
      filename: "dist/stats.html",
      gzipSize: true,
      brotliSize: true,
      template: "treemap", // "sunburst" and "network" are also available
    }),
  ],
});

Für Create-React-App-Projekte oder bereits gebaute Bundles ohne erneuten Build eignet sich source-map-explorer, das vorhandene Source Maps ausliest und dieselbe Art von Treemap direkt aus dem dist-Verzeichnis erzeugt. Beide Werkzeuge lösen dieselbe Aufgabe für die Bundle Analyse: sie machen sichtbar, was sonst als abstrakte Gesamtgröße im Netzwerk-Tab der Entwicklertools verborgen bleibt, und erlauben es, gezielt die größten Einzelposten zu identifizieren, statt blind zu optimieren.

3. Bundle-Größen-Budgets definieren und in CI durchsetzen

Sichtbarkeit allein verhindert keine Regression, wenn niemand regelmäßig hinschaut. Ein Bundle-Größen-Budget macht daraus eine automatisierte Prüfung: überschreitet ein Build eine definierte Grenze, schlägt die CI-Pipeline fehl, bevor der Pull Request gemerged werden kann. Für Bundle Analyse in der Praxis ist bundlesize oder das modernere size-limit die gängige Wahl.


{
  "size-limit": [
    {
      "name": "Main bundle (gzip)",
      "path": "dist/assets/index-*.js",
      "limit": "180 KB",
      "gzip": true
    },
    {
      "name": "Checkout route chunk",
      "path": "dist/assets/checkout-*.js",
      "limit": "60 KB",
      "gzip": true
    },
    {
      "name": "Vendor chunk",
      "path": "dist/assets/vendor-*.js",
      "limit": "220 KB",
      "gzip": true
    }
  ]
}

In der CI-Pipeline genügt danach ein einziger Aufruf, um jeden Pull Request gegen diese Budgets zu prüfen: npm run build && npx size-limit. Überschreitet ein Chunk seine Grenze, beendet sich der Befehl mit einem Nicht-Null-Exit-Code, wodurch der CI-Schritt fehlschlägt und der Pull Request nicht gemerged werden kann, bevor die Bundle-Größe wieder innerhalb der Budgets liegt.

Der entscheidende Effekt dieser Budgets für Bundle Analyse: eine neue, schwere Abhängigkeit fällt sofort im Pull Request auf, bevor sie überhaupt gemerged wird, statt erst Monate später bei einer aufwendigen Nachanalyse entdeckt zu werden. Die Grenzwerte sollten realistisch anhand des aktuellen Ist-Zustands gesetzt werden, mit etwas Puffer, statt willkürlich niedrig, um ständige Fehlalarme zu vermeiden.

4. Doppelte Abhängigkeiten und Versionskonflikte aufspüren

Ein häufig übersehener Bundle-Größen-Treiber, den eine oberflächliche Bundle Analyse leicht verpasst, sind doppelte Versionen derselben Bibliothek im Bundle. Das passiert typischerweise, wenn zwei verschiedene Abhängigkeiten jeweils eine eigene, inkompatible Version derselben Bibliothek als Peer Dependency verlangen, sodass der Package Manager beide Versionen parallel installiert.

Der Treemap-Visualizer aus Abschnitt zwei zeigt solche Duplikate meist bereits optisch als zwei separate Blöcke mit demselben Namen, aber unterschiedlicher Version. Zusätzlich hilft npm ls <paketname>, alle installierten Versionen eines Pakets im gesamten Abhängigkeitsbaum aufzulisten. Für Bundle Analyse-Zwecke ist außerdem das Rollup-Plugin rollup-plugin-duplicate-package-checker-plugin nützlich, das den Build fehlschlagen lässt, sobald mehrere Versionen desselben Pakets im Ausgabe-Bundle landen. Die Lösung ist meist ein overrides-Eintrag in package.json, der den Package Manager zwingt, eine einheitliche Version zu verwenden.

5. Route-basiertes Code Splitting systematisch auditieren

Route-basiertes Code Splitting mit React.lazy gehört zu den wirkungsvollsten Hebeln gegen aufgeblähte initiale Bundles, wird aber in gewachsenen Codebasen häufig nur an einigen Stellen konsequent umgesetzt und an anderen vergessen. Ein systematischer Audit prüft jede Top-Level-Route der Anwendung darauf, ob sie tatsächlich per lazy() geladen wird oder versehentlich Teil des Hauptbundles geblieben ist.


// router.tsx — auditing route-level code splitting
import { lazy, Suspense } from "react";
import { createBrowserRouter } from "react-router-dom";

// Correct: each route is its own chunk, loaded on demand
const CheckoutPage = lazy(() => import("./pages/CheckoutPage"));
const AdminDashboard = lazy(() => import("./pages/AdminDashboard"));

// Anti-pattern found during an audit: direct import keeps this
// in the main bundle even though it is rarely visited
// import { ReportsPage } from "./pages/ReportsPage";
const ReportsPage = lazy(() => import("./pages/ReportsPage"));

export const router = createBrowserRouter([
  {
    path: "/checkout",
    element: (
      <Suspense fallback={<PageSkeleton />}>
        <CheckoutPage />
      </Suspense>
    ),
  },
  {
    path: "/admin",
    element: (
      <Suspense fallback={<PageSkeleton />}>
        <AdminDashboard />
      </Suspense>
    ),
  },
  {
    path: "/reports",
    element: (
      <Suspense fallback={<PageSkeleton />}>
        <ReportsPage />
      </Suspense>
    ),
  },
]);

Bei der Bundle Analyse zeigt sich ein solcher Anti-Pattern deutlich im Treemap: eine Route, die im Alltag selten besucht wird, etwa ein Admin-Bereich oder eine Reporting-Seite, taucht trotzdem im Hauptbundle auf und wird von jedem Besucher geladen, unabhängig davon, ob er die Route je aufruft. Das systematische Auditieren aller Routen gegen diese Checkliste ist eine der ertragreichsten Maßnahmen bei jeder wiederkehrenden Bundle Analyse.

6. Tree Shaking: warum es oft nicht greift

Tree Shaking soll ungenutzten Code automatisch aus dem Bundle entfernen, funktioniert aber nur zuverlässig, wenn bestimmte Voraussetzungen erfüllt sind. Der häufigste Grund, warum Tree Shaking bei einer Bundle Analyse nicht wie erwartet greift, ist der Import-Stil: import _ from "lodash" zieht die komplette Bibliothek in das Bundle, während import debounce from "lodash/debounce" ausschließlich diese eine Funktion einbindet.

Ein zweiter, subtilerer Grund sind Seiteneffekte in importierten Modulen. Bundler müssen konservativ vorgehen und dürfen Code nicht entfernen, wenn dessen Import theoretisch einen Seiteneffekt auslösen könnte, etwa eine globale Registrierung. Das Feld "sideEffects": false in package.json einer eigenen Bibliothek signalisiert dem Bundler explizit, dass ungenutzte Exporte gefahrlos entfernt werden dürfen. Für Bundle Analyse-Zwecke lohnt sich eine gezielte Prüfung, ob häufig genutzte Bibliotheken wie Icon-Sets oder UI-Kits dieses Feld korrekt setzen, da fehlendes Tree Shaking gerade bei großen Icon-Bibliotheken zu unnötig aufgeblähten Bundles führt.

7. Dynamische Imports für schwere Bibliotheken

Manche Bibliotheken sind grundsätzlich schwer, unabhängig von Tree Shaking, etwa Chart-Bibliotheken, Rich-Text-Editoren oder PDF-Generatoren. Für diese lohnt sich ein dynamischer Import, der die Bibliothek erst lädt, wenn die entsprechende Funktion tatsächlich genutzt wird, statt sie beim initialen Seitenaufruf mitzuladen, selbst wenn die Funktion nie aufgerufen wird.


// ChartWidget.tsx — loading a heavy charting library only when needed
import { useState, useEffect, type ComponentType } from "react";

interface ChartProps {
  data: number[];
}

export function ChartWidget({ data }: ChartProps) {
  const [ChartComponent, setChartComponent] = useState<ComponentType<ChartProps> | null>(null);

  useEffect(() => {
    // recharts (roughly 90 KB gzipped) is only fetched once this component mounts
    import("./RechartsWrapper").then((module) => {
      setChartComponent(() => module.default);
    });
  }, []);

  if (!ChartComponent) {
    return <div className="h-64 animate-pulse bg-slate-100 rounded-lg" />;
  }

  return <ChartComponent data={data} />;
}

Für eine gründliche Bundle Analyse lohnt sich eine Liste aller Bibliotheken über einer bestimmten Größenschwelle, etwa 30 Kilobyte gzippt, mit der Frage, ob jede davon wirklich beim initialen Laden gebraucht wird. Chart-Bibliotheken, die nur auf einem Dashboard-Tab sichtbar sind, PDF-Export-Funktionen, die nur bei Klick auf einen Button ausgelöst werden, und Rich-Text-Editoren, die nur im Admin-Bereich vorkommen, sind klassische Kandidaten für dynamische Imports.

8. Kontinuierliches Monitoring in Pull Requests

Damit Bundle Analyse tatsächlich zum wiederkehrenden Prozess wird statt zur einmaligen Aktion, muss sie in den täglichen Entwicklungsworkflow integriert werden. Ein GitHub-Actions-Bot, der die Bundle-Größenänderung direkt als Kommentar in jedem Pull Request postet, macht die Auswirkung einer Änderung sofort sichtbar, ohne dass jemand manuell einen Report öffnen muss.


# .github/workflows/bundle-comment.yml
name: Bundle Size Comment

on:
  pull_request:
    branches: [main]

jobs:
  comment-bundle-diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - name: Compare bundle size against main
        uses: andresz1/size-limit-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          script: "npm run build"

Diese Automatisierung ist der wichtigste Schritt, um Bundle Analyse von einer sporadischen Übung in einen festen Bestandteil des Reviews zu verwandeln. Reviewer sehen direkt im Pull Request, ob eine Änderung das Hauptbundle um 500 Bytes oder um 40 Kilobyte vergrößert, und können bei größeren Sprüngen gezielt nachfragen, ob ein dynamischer Import oder ein kleinerer Ersatz für eine neue Abhängigkeit infrage kommt.

9. Analyse-Tools im Überblick

Für unterschiedliche Zwecke innerhalb eines Bundle Analyse-Prozesses eignen sich unterschiedliche Werkzeuge. Die folgende Tabelle ordnet die wichtigsten Optionen nach ihrem primären Anwendungsfall.

Tool Zweck Läuft in CI
rollup-plugin-visualizer Interaktive Treemap nach jedem Build Optional, meist manuell genutzt
source-map-explorer Treemap aus bestehenden Source Maps Optional, meist manuell genutzt
size-limit Harte Budgets pro Chunk durchsetzen Ja, blockiert Merge bei Überschreitung
duplicate-package-checker Doppelte Paketversionen erkennen Ja, als Build-Schritt
size-limit-action Größenänderung als PR-Kommentar posten Ja, primär für CI gedacht

In der Praxis ergänzen sich diese Tools: Visualizer-Tools dienen der punktuellen, tiefen Analyse bei größeren Optimierungsrunden, während size-limit und Duplicate-Checker die kontinuierliche Überwachung in der CI-Pipeline übernehmen. Eine vollständige Bundle Analyse-Strategie kombiniert beide Kategorien, statt sich nur auf eine zu verlassen.

Mironsoft

Bundle Analyse, Code Splitting und Performance-Audits für React

Wächst euer React Bundle unbemerkt von Release zu Release?

Wir führen eine vollständige Bundle Analyse eurer React App durch, identifizieren doppelte Abhängigkeiten und fehlendes Code Splitting, und richten CI-Budgets ein, die Regressionen dauerhaft verhindern.

Bundle-Audit

Tiefgehende Analyse mit Visualizer-Tools und konkreten Optimierungsvorschlägen

CI-Budgets einrichten

Größen-Budgets pro Chunk, die Regressionen automatisch im Pull Request blockieren

Code-Splitting-Rollout

Route-basiertes Splitting und dynamische Imports systematisch nachrüsten

10. Zusammenfassung

Bundle Analyse entfaltet ihren Wert erst als wiederkehrender Audit-Prozess, nicht als einmalige Aufräumaktion. Visualizer-Tools wie rollup-plugin-visualizer und source-map-explorer schaffen die nötige Sichtbarkeit, während size-limit in der CI-Pipeline harte Grenzen durchsetzt, bevor eine Regression überhaupt gemerged werden kann.

Route-basiertes Code Splitting mit React.lazy, konsequentes Tree Shaking durch korrekten Import-Stil und dynamische Imports für schwere Bibliotheken sind die konkreten technischen Hebel, die eine Bundle Analyse typischerweise aufdeckt. Wer diese Prüfungen automatisiert in Pull Requests integriert, verhindert das schleichende Wachstum, das ohne Kontrolle jede React-Anwendung über Monate hinweg spürbar langsamer macht.

Bundle Analyse und Code Splitting Audits — Das Wichtigste auf einen Blick

Sichtbarkeit schaffen

rollup-plugin-visualizer und source-map-explorer zeigen als Treemap, welches Modul wie viel Bundle-Größe belegt.

CI-Budgets durchsetzen

size-limit prüft jeden Pull Request gegen definierte Grenzen und blockiert Merges bei Überschreitung.

Doppelte Abhängigkeiten

Versionskonflikte führen zu doppelt gebündelten Bibliotheken, npm ls und Duplicate-Checker decken sie auf.

Route-Splitting und Dynamic Import

React.lazy pro Route und dynamische Imports für schwere Bibliotheken reduzieren das initiale Bundle spürbar.

11. FAQ: Bundle Analyse und Code Splitting in React

1Wie oft sollte Bundle Analyse laufen?
Kontinuierlich über CI-Budgets pro Pull Request, ergänzt um tiefere manuelle Analysen alle paar Monate.
2Visualizer versus Source Map Explorer?
Visualizer generiert die Treemap während des Builds, Source Map Explorer liest bestehende Source Maps aus.
3Wie setzt man ein Größen-Budget?
Am aktuellen Ist-Zustand mit etwas Puffer orientieren, nicht willkürlich niedrig ansetzen.
4Wie erkennt man doppelte Abhängigkeiten?
Im Treemap als zwei Blöcke mit gleichem Namen, unterschiedlicher Version. npm ls listet alle Versionen auf.
5Warum greift Tree Shaking manchmal nicht?
Häufig wegen falschem Import-Stil oder fehlendem sideEffects: false in package.json.
6Wann dynamischer Import?
Bei schweren, selten genutzten Bibliotheken wie Chart-Libraries oder PDF-Generatoren.
7Was ist route-basiertes Code Splitting?
Jede Route wird über React.lazy als eigener Chunk geladen, statt im Hauptbundle zu verbleiben.
8Wie postet man Größenänderungen im PR?
Mit einer GitHub Action wie size-limit-action, die das Ergebnis als Kommentar im Pull Request hinterlässt.
9Reicht Gzip zur Ignorierung der Bundle-Größe?
Nein, Kompression reduziert die Übertragung, nicht die Parse- und Ausführungszeit im Browser.
10Welche Kennzahlen leiden am stärksten?
Vor allem Time to Interactive und Interaction to Next Paint durch lange Parse- und Ausführungszeiten.