Files
archivmail/features/PROJ-77-admin-tabs-dynamic-import.md
T
sysopsandClaude Sonnet 5 e59b11743a perf(PROJ-77): Admin-Tabs per next/dynamic laden statt statisch importieren
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
2026-08-05 15:05:24 +02:00

83 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: PROJ-77
title: Admin-Tab-Bundle-Optimierung (dynamic import statt 19 statische Imports)
status: In Review
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: () => <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 2239) 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 = () => <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, mit `aria-busy`/`aria-live` und 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
- [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.