Case-Study · React 18 SPA, Vite, TanStack Router, B2B-Dashboard
Case-Study: React-Bundle radikal verkleinert
B2B-Dashboard. Vorher 470 KB Initial-JS, nachher 110 KB. INP fiel von 540 ms auf 180 ms.
Metriken vor / nach
Lighthouse
LCP
INP
CLS
Maßnahmen
- ✓ moment.js → date-fns (286 KB Einsparung)
- ✓ lodash full → lodash-es named imports (52 KB)
- ✓ React.lazy für 80 % der Routes
- ✓ Recharts → Tremor/SVG-eigen (40 KB)
- ✓ Vendor-Bundle aufgesplittet in 5 Chunks
B2B-Dashboard mit ~3.000 täglichen Sessions. React 18 SPA, gebaut mit Vite 4. Lighthouse-Score Mobile bei 64, INP im Field-Bereich bei 540 ms (rot). Bundle-Analyzer zeigte initial 470 KB komprimiert.
Bundle-Audit
Vite-Bundle-Visualizer zeigte folgende Top-Brocken:
- moment.js + moment-timezone: 286 KB (60 % des Bundles!)
- recharts: 142 KB
- lodash (full import): 72 KB
- react + react-dom: 130 KB
- App-Code: 80 KB
Maßnahme 1: moment.js → date-fns (-280 KB)
Größter Einzelschritt. Die App nutzte ~12 verschiedene moment-Funktionen (format, fromNow, add, subtract, diff, isBefore, isAfter, etc.). Migration auf date-fns mit tree-shaking dauerte einen halben Tag. Vorher importiert 290 KB für 12 Funktionen, nachher 8 KB für die gleichen Funktionen.
Maßnahme 2: lodash full → lodash-es (-52 KB)
import _ from 'lodash' bundelt alle 200+ Funktionen, davon brauchte die App ~15 (debounce, throttle, groupBy, omit, pick, etc.). Switch auf import { debounce } from 'lodash-es'. Tree-Shaking reduzierte den Footprint auf 20 KB.
Maßnahme 3: React.lazy für Route-Chunks (-180 KB initial)
Vorher waren alle 14 Routes synchron importiert. Mit React.lazy + Suspense wurden 11 davon async. Initial-Route lädt nur noch ihre eigenen Komponenten + den Vendor-Core. Andere Routes lädt der Browser bei Navigation nach.
Maßnahme 4: Recharts → Tremor (-100 KB)
Recharts ist mächtig aber schwer (142 KB). Die App nutzte nur 4 Chart-Typen (Bar, Line, Donut, Number). Migration auf Tremor (~40 KB) + zwei custom-SVG-Components für sehr spezielle Fälle. Visuelle Qualität bleibt gleich.
Maßnahme 5: Vendor-Chunk-Splitting
Vite-Config angepasst: manualChunks für react, react-router, tanstack/query, charts und shared-utils. Statt einem 200 KB Vendor-Bundle: 5 kleinere Chunks die der Browser parallel lädt und einzeln cached.
Ergebnis
| Metrik | Vorher | Nachher |
|---|---|---|
| Initial JS (gzip) | 470 KB | 110 KB |
| LCP | 2,8 s | 1,8 s |
| INP | 540 ms | 180 ms |
| Lighthouse Mobile | 64 | 89 |
Aufwand: 2 Tage. moment.js-Migration war 60 % der Arbeit, restliche Maßnahmen je 1-2 Stunden.