npm run lint rief next lint auf, das es in Next.js 16 nicht mehr gibt — seit dem Next-16-Upgrade lief effektiv gar kein Lint mehr. Umgestellt auf eslint . mit Flat-Config (eslint.config.mjs statt .eslintrc.json). Der dadurch wieder sichtbare Lint-Lauf zeigte 30 Findings (25 Fehler, 5 Warnungen), alle gefixt: - 19x react-hooks/set-state-in-effect: Loading-States wo möglich als echte Ableitung statt eigenem Effect-State (use-mobile.tsx komplett auf useSyncExternalStore umgebaut), sonst async-Wrapper mit Cancel-Guard um bestehende Loader — Timing/Ladeanzeige unverändert. - react-hooks/refs (useSearch.ts): Ref-Schreibzugriff aus dem Render in einen Effect verschoben. - 4x no-html-link-for-pages: <a href> durch next/link ersetzt in admin/login, forgot-password, signup. - Rest (exhaustive-deps, no-img-element, unused disable) einzeln gefixt. - 4 bewusst belassene disable-Kommentare mit Begründung (shadcn/ui-Datei, QR-Code-data-URL, Full-Reload nach Auth laut Projektregel). eslint-Major-Upgrade auf 10 selbst bleibt blockiert: eslint-plugin-react/ jsx-a11y/import unterstützen ESLint 10 in ihrer aktuellen Latest-Version noch nicht (Crash beim Laden), siehe Feature-Spec PROJ-79. Verifiziert auf 132 (Build-Sandbox, kein Live-Deploy): npm ci/tsc/lint/ build grün, 8 Kern-Routen per Standalone-Server auf HTTP 200 geprüft. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
3.1 KiB
id, title, status, created
| id | title | status | created |
|---|---|---|---|
| PROJ-77 | Admin-Tab-Bundle-Optimierung (dynamic import statt 19 statische Imports) | Deployed | 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.
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
- 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.