Progressive Web-UI
Bei der wahrgenommenen Performance (Perceived Performance) geht es darum, dass sich die UI schnell anfühlt, nicht nur darum, dass sie gemessen schnell ist. Eine Seite, die in 2s lädt, aber sofort ein Skelett anzeigt, fühlt sich schneller an als eine, die in 1,5s lädt, aber die ganze Zeit einen leeren Bildschirm zeigt.
Wenn der Benutzer Code teilt:
- Lies ihn, um zu verstehen, was Benutzer aktuell während Lade- und Wartezuständen sehen
- Identifiziere mithilfe des untenstehenden Entscheidungsleitfadens, welche Technik(en) zutreffen
- Erkläre was geändert werden muss, warum es die wahrgenommene Geschwindigkeit verbessert und wie es mit konkretem Code umgesetzt wird
Kernprinzipien
1. Trenne „erste Ansicht“ von allem anderen
Unterscheide immer:
- Was für den ersten Paint erforderlich ist
- Was geladen werden kann, nachdem das Shell sichtbar ist
- Was auf Benutzerinteraktion warten kann
Behandle nicht alle Daten als gleich dringend.
2. Struktur vor Daten
Das Seiten-Shell (Layout, Nav-Slots, Header-Rahmen) sollte an den Browser gesendet werden, bevor ein Datenabruf abgeschlossen ist. Benutzer bilden ihren Geschwindigkeitseindruck in den ersten 100–200 ms. Das sofortige Zeigen der Struktur reduziert die gefühlte Wartezeit, selbst wenn die tatsächliche Ladezeit identisch ist.
3. Lasse nie einen Klick unbeantwortet
Jede Interaktion (Klick, Tippen, Navigation) muss im ersten Frame eine sichtbare Reaktion hervorrufen. Wenn die eigentliche Operation Zeit braucht, zeige sofort einen Zwischenzustand an.
4. Behebe die Struktur, nicht nur das Lade-UI
loading.tsx und Skelette verbessern das Erscheinungsbild, beheben aber nicht das Über-Abrufen oder sequentielle Datenabhängigkeiten. Gehe immer der Ursache auf den Grund:
- Sequentielle
await-Ketten → parallelisieren mitPromise.all - Einzelner großer Abruf → aufteilen in Erstansicht vs. zurückgestellt
- Vollständiges Profil bei Navigation → leichten Navigations-Identitätshelfer verwenden
5. Navigation muss sich auch schnell anfühlen
Die wahrgenommene Geschwindigkeit betrifft nicht nur den Erstladevorgang. Seitenübergänge, wiederholte Besuche und Interaktionen müssen sich gleichermaßen schnell anfühlen:
- Prefetch-Routen, bevor der Benutzer klickt
loading.tsxsofort bei Navigationsstart anzeigen- SWR/Cache für sofortige Wiederholungsbesuche nutzen
Entscheidungsleitfaden
| Symptom | Technik |
|---|---|
| Leerer Bildschirm während Daten laden | Skeleton-Screens / Suspense Streaming |
| Button-Klick hat keine sofortige Reaktion | Optimistisches UI / sofortiges Feedback |
| Seitenübergang startet nach Klick nicht visuell | loading.tsx + Prefetch |
| Schweres Komponente verlangsamt initiales Rendern | Dynamischer Import |
| Spinner erscheint bei jedem Besuch derselben Daten | SWR Stale-While-Revalidate |
| Eine langsame API blockiert eine gesamte Seite | Suspense Streaming (parallele async Server Components) |
| Auth/Profile-Abruf verzögert Navigation oder Seiten-Shell | Auth-Layout-Trennung (siehe references/auth-performance.md) |
| Bilder verursachen Layout-Verschiebungen oder fühlen sich langsam an | next/image mit priority / blur-Platzhalter |
| Schriftartwechsel verursacht Flash of Unstyled Text | next/font |
| Formularabsendung hat kein visuelles Feedback | useFormStatus / useActionState |
| Lange Liste verursacht Scroll-Ruckeln oder langsames Rendern | Virtuelles Scrollen (TanStack Virtual / react-window) |
| Nicht dringender Zustandswechsel blockiert Benutzereingabe | startTransition für verzögertes Rendern |
Analyse-Workflow
Schritt 1: Identifiziere, was Benutzer zuerst sehen
Was rendert in 0–200 ms? Wenn es leer oder ein Spinner ist, sind strukturelle Änderungen nötig, bevor Styling-Fixes helfen.
Schritt 2: Klassifiziere Daten nach Dringlichkeit
- Erste Ansicht – muss vorhanden sein, damit die Seite sinnvoll ist
- Zurückgestellt – kann nach dem Shell sichtbar hereinströmen
- Auf Abruf – nur nach Benutzerinteraktion laden
Schritt 3: Behebe Datenabhängigkeiten
- Sequentielle
await→Promise.alloder separate async Server Components - Über-Abrufen beim initialen Laden → API aufteilen oder paginieren
- Vollständiges Profil bei Navigation → separaten Navigations-Identitätshelfer
Schritt 4: Wende progressives Rendern an
Wähle basierend darauf, was benötigt wird:
loading.tsx– sofortiges Navigations-Skelett (ganze Seite)<Suspense>– granuläres Streaming (pro Abschnitt)useOptimistic– sofortiges Mutations-Feedbackdynamic()– schweres Komponentenbündel zurückstellen- SWR – sofortige Wiederholungsbesuche aus dem Cache
Schritt 5: Validiere die wahrgenommene Performance
- Erscheint sofort etwas Sinnvolles (< 200 ms)?
- Wird der Benutzer während aller Ladezustände informiert?
- Ist die UI reaktionsfähig, bevor alle Daten ankommen?
- Erzeugt die Interaktion im ersten Frame Feedback?
Ausgabeformat
Wenn du dieses Skill anwendest, antworte in dieser Struktur:
## Was gerade passiert
- [beschreibe, was Benutzer aktuell erleben]
## Ursache
- [was im Code das langsame Gefühl verursacht]
## Was geändert werden muss und warum
- [Änderung 1]: [warum dies die wahrgenommene Geschwindigkeit verbessert]
- [Änderung 2]: ...
## Implementierung
[code]
## Hinweise
- [eventuelle Einschränkungen, Randfälle oder Folgeverbesserungen]
Referenzen
Ausführliche Implementierungsdetails mit vollständigen Codebeispielen findest du in:
references/nextjs-patterns.md– Skeleton-Screens, Optimistisches UI, Suspense Streaming, Prefetch, Dynamischer Import, SWR, Bilder, Schriften, Interaktions-Feedback,useFormStatus, Virtuelles Scrollen,startTransitionreferences/auth-performance.md– Auth-Layout-Trennung, Navigations-Identitätshelfer, Request/Process-Warm-Up, Fallback-Zustandsdesign
Lese die entsprechende Referenz, wenn du eine bestimmte Technik implementierst.
Anti-Patterns zum Beheben
Erkenne und behebe immer:
- Abrufen des vollständigen Profils/Auth im Top-Level-Layout → verzögert jede Seite
- Sequentielle
await-Ketten für unabhängige Daten → unnötige Serialisierung loading.tsxhinzugefügt, ohne das zugrundeliegende Über-Abrufen zu beheben- Rendern vollständiger Datensätze beim initialen Laden → paginieren oder virtualisieren
- Kein visuelles Feedback bei Klick, bis die Operation abgeschlossen ist → sofort bestätigen
- Unterschiedliche Auth-Quellen in Navigation vs. Seite → kanonische Inkonsistenz des Benutzernamens
- Formular-Absendestatus nur nach Serverantwort verwalten →
useFormStatusfür sofortigen Wartezustand nutzen - Rendern aller Elemente einer langen Liste beim Mount → virtualisieren (nur sichtbare Zeilen rendern)
- Teure Zustandsänderung (Filter/Sortieren/Suche) blockiert Benutzereingabe → in
startTransitioneinwickeln