---
id: PROJ-77
title: Admin-Tab-Bundle-Optimierung (dynamic import statt 19 statische Imports)
status: Deployed
created: 2026-08-05
---
## Problem
`src/app/admin/page.tsx` (Zeile ~22-40) importiert alle 19 Admin-Tab-
Komponenten statisch. Aktuell gibt es im gesamten Projekt 0 `next/dynamic`-
Imports. Radix `TabsContent` unmountet zwar inaktive Tabs zur Laufzeit
(kein gleichzeitiges Rendern), aber das initiale JS-Bundle enthält trotzdem
den Code aller 19 Tabs, auch wenn ein Nutzer nur einen einzigen Tab je
Sitzung öffnet.
Kein Crash-Bezug, reine Bundle-Größen-/Ladezeit-Optimierung.
## Lösung (Vorschlag)
Jede Admin-Tab-Komponente auf `next/dynamic` mit Loading-State umstellen,
z.B.:
```ts
const DashboardTab = dynamic(() => import('@/components/admin/tabs/DashboardTab'), {
loading: () => ,
})
```
Umfang: 19 Komponenten, jeweils Import-Umstellung + einheitlicher
Loading-Skeleton. Vor Umsetzung: kurzer Bundle-Size-Vergleich
(`next build` Output) vorher/nachher, um den tatsächlichen Gewinn zu
beziffern.
## Implementation Notes
Umgesetzt am 2026-08-05.
**Geänderte/neue Dateien**
- `src/app/admin/page.tsx`: 18 statische Tab-Imports (Zeile 22–39) durch
`next/dynamic`-Deklarationen ersetzt. Alle Tab-Komponenten sind Named
Exports, daher jeweils
`dynamic(() => import("…/XyzTab").then((m) => m.XyzTab), { loading })`.
Eine gemeinsame `const loading = () => ` wird für alle
18 Tabs wiederverwendet. `ResetPasswordDialog`/`DeleteUserDialog`
(Global-Dialoge, immer sichtbar) bleiben bewusst statisch.
- `src/components/admin/TabSkeleton.tsx` (neu): einheitlicher
Lade-Platzhalter auf Basis der vorhandenen shadcn/ui-`Skeleton`-
Komponente, mit `aria-busy`/`aria-live` und sr-only-Text.
**Props-Typisierung:** `dynamic()` inferiert die Props über den
`.then()`-Rückgabewert korrekt; ein explizites `ComponentType`
war nicht nötig. `npm run build` (inkl. TypeScript-Check) läuft fehlerfrei.
**Bundle-Size-Vergleich** (Next.js 16 / Turbopack zeigt keine Größen im
Build-Output; gemessen als Summe der im prerenderten
`.next/server/app/admin.html` referenzierten `_next/static/*.js`-Dateien,
unkomprimiert):
| | Dateien | Initiales JS |
|---|---|---|
| vorher | 17 | 1035,6 KB |
| nachher | 18 | 830,7 KB |
| Delta | +1 | **−204,9 KB (−19,8 %)** |
Der Code der Tabs liegt jetzt in separaten Chunks, die erst beim
Aktivieren des jeweiligen Tabs geladen werden.
Deployed auf 131 (Produktiv) am 2026-08-05.
**Hinweis:** `dynamic()` läuft hier mit Default-`ssr: true`, d.h. der
initial aktive Tab wird weiterhin serverseitig vorgerendert — kein
Flash-of-Skeleton beim ersten Laden des Dashboards.
## Acceptance Criteria
- [x] Alle Admin-Tabs (18 Tab-Komponenten) per `next/dynamic` geladen,
mit Loading-Skeleton.
- [x] `npm run build` Bundle-Size-Report zeigt messbare Reduktion der
initialen Admin-Route-Chunk-Größe (−204,9 KB / −19,8 %).
- [ ] Funktionale Regression: alle Tabs weiterhin normal nutzbar
(manueller Klick-Test durch alle Tabs) — **noch zu verifizieren**,
kann nicht automatisiert erfolgen; offen für QA.