18 Admin-Tab-Komponenten wurden bisher alle statisch in src/app/admin/page.tsx importiert, unabhängig davon welcher Tab tatsächlich geöffnet wird. Umstellung auf next/dynamic mit gemeinsamem TabSkeleton-Loading-State reduziert das initiale JS-Bundle der Admin-Route um 204,9 KB (-19,8%). Initial aktiver Tab bleibt weiterhin serverseitig vorgerendert (ssr: true), kein Skeleton-Flash beim ersten Laden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
3.0 KiB
id, title, status, created
| id | title | status | created |
|---|---|---|---|
| PROJ-77 | Admin-Tab-Bundle-Optimierung (dynamic import statt 19 statische Imports) | In Review | 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.:
const DashboardTab = dynamic(() => import('@/components/admin/tabs/DashboardTab'), {
loading: () => <TabSkeleton />,
})
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) durchnext/dynamic-Deklarationen ersetzt. Alle Tab-Komponenten sind Named Exports, daher jeweilsdynamic(() => import("…/XyzTab").then((m) => m.XyzTab), { loading }). Eine gemeinsameconst loading = () => <TabSkeleton />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, mitaria-busy/aria-liveund sr-only-Text.
Props-Typisierung: dynamic() inferiert die Props über den
.then()-Rückgabewert korrekt; ein explizites ComponentType<Props>
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.
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
- Alle Admin-Tabs (18 Tab-Komponenten) per
next/dynamicgeladen, mit Loading-Skeleton. npm run buildBundle-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.