diff --git a/features/PROJ-77-admin-tabs-dynamic-import.md b/features/PROJ-77-admin-tabs-dynamic-import.md
new file mode 100644
index 0000000..1952174
--- /dev/null
+++ b/features/PROJ-77-admin-tabs-dynamic-import.md
@@ -0,0 +1,82 @@
+---
+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: () => ,
+})
+```
+
+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.
+
+**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.
diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx
index 2945b7f..2537456 100644
--- a/src/app/admin/page.tsx
+++ b/src/app/admin/page.tsx
@@ -1,6 +1,7 @@
"use client";
import { useEffect, useMemo, useState } from "react";
+import dynamic from "next/dynamic";
import { useAuth } from "@/hooks/useAuth";
import { useLDAPConfig } from "@/hooks/useLDAPConfig";
import { useTenantLDAPConfig } from "@/hooks/useTenantLDAPConfig";
@@ -18,26 +19,85 @@ import { Navbar } from "@/components/navbar";
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
-// Tab components
-import { DashboardTab } from "@/components/admin/tabs/DashboardTab";
-import { ServicesTab } from "@/components/admin/tabs/ServicesTab";
-import { UsersTab } from "@/components/admin/tabs/UsersTab";
-import { AuditTab } from "@/components/admin/tabs/AuditTab";
-import { ImportTab } from "@/components/admin/tabs/ImportTab";
-import { SecurityTab } from "@/components/admin/tabs/SecurityTab";
-import { LDAPTab } from "@/components/admin/tabs/LDAPTab";
-import { TenantLDAPTab } from "@/components/admin/tabs/TenantLDAPTab";
-import { TenantsTab } from "@/components/admin/tabs/TenantsTab";
-import { CertTab } from "@/components/admin/tabs/CertTab";
-import { ModulesTab } from "@/components/admin/ModulesTab";
-import { IMAPSettingsTab } from "@/components/admin/tabs/IMAPSettingsTab";
-import { RetentionTab } from "@/components/admin/tabs/RetentionTab";
-import { ArchivingRulesTab } from "@/components/admin/tabs/ArchivingRulesTab";
-import { RoutingRulesTab } from "@/components/admin/tabs/RoutingRulesTab";
-import { QuotaTab } from "@/components/admin/tabs/QuotaTab";
-import { SMTPOutTab } from "@/components/admin/tabs/SMTPOutTab";
-import { DSGVOTab } from "@/components/admin/tabs/DSGVOTab";
import { ResetPasswordDialog, DeleteUserDialog } from "@/components/admin/UserDialogs";
+import { TabSkeleton } from "@/components/admin/TabSkeleton";
+
+// Tab components — dynamisch nachgeladen (PROJ-77), damit das initiale
+// Admin-Bundle nicht den Code aller 19 Tabs enthält.
+const loading = () => ;
+
+const DashboardTab = dynamic(
+ () => import("@/components/admin/tabs/DashboardTab").then((m) => m.DashboardTab),
+ { loading }
+);
+const ServicesTab = dynamic(
+ () => import("@/components/admin/tabs/ServicesTab").then((m) => m.ServicesTab),
+ { loading }
+);
+const UsersTab = dynamic(
+ () => import("@/components/admin/tabs/UsersTab").then((m) => m.UsersTab),
+ { loading }
+);
+const AuditTab = dynamic(
+ () => import("@/components/admin/tabs/AuditTab").then((m) => m.AuditTab),
+ { loading }
+);
+const ImportTab = dynamic(
+ () => import("@/components/admin/tabs/ImportTab").then((m) => m.ImportTab),
+ { loading }
+);
+const SecurityTab = dynamic(
+ () => import("@/components/admin/tabs/SecurityTab").then((m) => m.SecurityTab),
+ { loading }
+);
+const LDAPTab = dynamic(
+ () => import("@/components/admin/tabs/LDAPTab").then((m) => m.LDAPTab),
+ { loading }
+);
+const TenantLDAPTab = dynamic(
+ () => import("@/components/admin/tabs/TenantLDAPTab").then((m) => m.TenantLDAPTab),
+ { loading }
+);
+const TenantsTab = dynamic(
+ () => import("@/components/admin/tabs/TenantsTab").then((m) => m.TenantsTab),
+ { loading }
+);
+const CertTab = dynamic(
+ () => import("@/components/admin/tabs/CertTab").then((m) => m.CertTab),
+ { loading }
+);
+const ModulesTab = dynamic(
+ () => import("@/components/admin/ModulesTab").then((m) => m.ModulesTab),
+ { loading }
+);
+const IMAPSettingsTab = dynamic(
+ () => import("@/components/admin/tabs/IMAPSettingsTab").then((m) => m.IMAPSettingsTab),
+ { loading }
+);
+const RetentionTab = dynamic(
+ () => import("@/components/admin/tabs/RetentionTab").then((m) => m.RetentionTab),
+ { loading }
+);
+const ArchivingRulesTab = dynamic(
+ () => import("@/components/admin/tabs/ArchivingRulesTab").then((m) => m.ArchivingRulesTab),
+ { loading }
+);
+const RoutingRulesTab = dynamic(
+ () => import("@/components/admin/tabs/RoutingRulesTab").then((m) => m.RoutingRulesTab),
+ { loading }
+);
+const QuotaTab = dynamic(
+ () => import("@/components/admin/tabs/QuotaTab").then((m) => m.QuotaTab),
+ { loading }
+);
+const SMTPOutTab = dynamic(
+ () => import("@/components/admin/tabs/SMTPOutTab").then((m) => m.SMTPOutTab),
+ { loading }
+);
+const DSGVOTab = dynamic(
+ () => import("@/components/admin/tabs/DSGVOTab").then((m) => m.DSGVOTab),
+ { loading }
+);
export default function AdminPage() {
const { user, loading: authLoading } = useAuth("domain_admin", "/admin/login");
diff --git a/src/components/admin/TabSkeleton.tsx b/src/components/admin/TabSkeleton.tsx
new file mode 100644
index 0000000..702ea92
--- /dev/null
+++ b/src/components/admin/TabSkeleton.tsx
@@ -0,0 +1,22 @@
+"use client";
+
+import { Skeleton } from "@/components/ui/skeleton";
+
+/**
+ * Einheitlicher Lade-Platzhalter für dynamisch nachgeladene Admin-Tabs.
+ * Wird von next/dynamic als `loading`-Komponente verwendet (PROJ-77).
+ */
+export function TabSkeleton() {
+ return (
+
+
Inhalt wird geladen …
+
+
+
+
+
+
+
+
+ );
+}