SWR – vollständiger Leitfaden: Cache-Strategien und optimistische Updates in der Praxis

Sie starren auf den vertrauten Loading-Spinner – zum N-ten Mal die Benutzerliste neu geladen. Tab wechseln: Loading. Von einer anderen Seite zurück: Loading. Sogar nach einem kurzen Telefonat, bei dem der Browser den Fokus verliert, ist beim Zurückkommen wieder Loading. Am frustrierendsten: Die Daten wurden vor einer Sekunde erst geladen.
In React-Projekten schreibt man für jeden Datenabruf erneut useState, useEffect plus Loading- und Error-Handling. Mindestens 20 Zeilen Code. Noch ärgerlicher: Mehrere Komponenten brauchen dieselben Daten – entweder State nach oben heben (mehr Code) oder jede Komponente fetcht selbst (verschwendete Netzwerkressourcen).
Als ich SWR entdeckte, war das Gefühl vergleichbar mit dem ersten Mal Git statt manuellem Ordner-Kopieren. Drei Zeilen Code, 80 % der Probleme gelöst. Keine Übertreibung.
Heute geht es um SWR – die React-Datenabruf-Bibliothek von Vercel. Das Kernprinzip heißt stale-while-revalidate (veraltet – neu validieren). Klingt technisch? Die Idee ist simpel: Zuerst sehen Sie das gecachte „alte Foto“ (schnell), im Hintergrund wird heimlich ein neues gemacht (aktuell), und wenn es fertig ist, wird es lautlos ausgetauscht (nahtlos).
Warum SWR? Drei Schmerzpunkte klassischer Datenabruf
Schauen wir uns den klassischen React-Datenabruf an. Angenommen, Sie wollen eine Benutzerliste anzeigen:
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
24 Zeilen Code – nur für eine Liste. Noch akzeptabel? Betrachten Sie diese Szenarien:
Schmerzpunkt 1: Wiederholter Boilerplate-Code
Jede datenabhängige Komponente braucht dasselbe Muster. Benutzerliste, Artikelliste, Kommentarliste – drei States, viele if-Abfragen, Error-Handling … Copy-Paste fühlt sich peinlich an. Einmal habe ich gezählt: In einem mittelgroßen Projekt machten solche Snippets etwa 20 % des Codes aus.
Schmerzpunkt 2: Cache? Fehlanzeige
Am bittersten: kein Cache. Vom Start zur Detailseite und zurück zur Startseite – wieder langes Loading. Die Daten waren vor 30 Sekunden da, trotzdem ein neuer Request. Nutzer sagen „Die Seite ist langsam“ – oft nicht, weil die API langsam ist, sondern weil gar nicht gecacht wird.
Man kann globales State-Management bauen – Redux, Zustand, Store, Actions, Reducer … Eine Funktion, die in drei Minuten fertig wäre, wird zu 30 Minuten Architektur.
Schmerzpunkt 3: Datensynchronisation über Komponenten
Mehrere Komponenten, dieselben Daten: Ungelesene Nachrichten in der Kopfzeile, in der Sidebar und auf der Nachrichtenliste. State bis ganz nach oben? Dann Props durch viele Ebenen. Context? Bei jeder Aktualisierung re-rendert der ganze Baum.
Einmal habe ich zwei Tage an einer simplen Benachrichtigungsfunktion verbracht – am Ende nur setState und useEffect im Diff. Das war mir selbst peinlich.
Deshalb ist SWR populär. Kein weiteres Rad – es löst echte Probleme. Derselbe Code mit SWR:
import useSWR from 'swr';
function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error!</div>;
return <ul>{data.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
9 Zeilen. Fertig. Mit Cache, automatischer Revalidierung und geteiltem State über Komponenten. Der fetcher ist Ihre fetch-Funktion – meist einmal global definiert:
const fetcher = url => fetch(url).then(r => r.json());
Der Unterschied spricht für sich.
Kernkonzept: Stale-While-Revalidate
Der Name SWR kommt aus der HTTP-Cache-Strategie in RFC 5861. Keine Angst vor „RFC“ – die Idee ist leicht.
Analogie mit Fotos: Sie wollen ein aktuelles Bild eines Freundes. Klassisch: anrufen, warten, bis er eins schickt. Mit SWR: zuerst das alte Foto vom letzten Treffen (vielleicht von letzter Woche), parallel Nachricht schicken „Schick mir ein neues“, bei Ankunft austauschen.
Der Unterschied: Sie warten nicht leer. Sofort Inhalt (evtl. etwas veraltet), parallel Sicherstellung der Aktualität. Das ist stale-while-revalidate – Veraltetes zeigen, gleichzeitig neu validieren.
Ablauf bei SWR:
-
Schritt 1: Cache sofort zurückgeben (stale)
- Beim Mount prüft SWR den lokalen Cache
- Cache vorhanden? Sofort zurück, Seite springt auf
- Kein Cache?
undefined, Loading anzeigen
-
Schritt 2: Request im Hintergrund (revalidate)
- Mit oder ohne Cache: API-Request
- Nutzer merkt es kaum, Inhalt ist schon da
-
Schritt 3: Daten aktualisieren
- Nach der Antwort: Cache und UI leise aktualisieren
- Bei Änderung: React re-rendert
- Unverändert: nichts passiert
Beispiel – Aktienkurs-Anzeige:
function StockPrice({ symbol }) {
const { data, error } = useSWR(`/api/stock/${symbol}`, fetcher);
return (
<div>
<h2>{symbol}</h2>
<p>Preis: {data ? `$${data.price}` : 'Loading...'}</p>
<span>Aktualisiert: {data?.updatedAt}</span>
</div>
);
}
Beim ersten Öffnen ist data undefined – „Loading…“. Nach einer Sekunde der Preis.
Tab wechseln, 10 Minuten E-Mails, zurück – Preis sofort aus dem Cache, kein Loading-Flackern. Parallel startet SWR einen neuen Request. Kurs geändert → UI aktualisiert; gleich → bleibt.
Drei Standard-Zeitpunkte für automatische Revalidierung:
- Komponente erneut gemountet (
revalidateOnMount): Reload oder Route zurück - Fenster wieder im Fokus (
revalidateOnFocus): Tab zurück zum Browser - Netzwerk wieder da (
revalidateOnReconnect): nach Offline
Sie müssen nichts extra bauen – SWR hält Daten frisch. Halbe Stunde E-Mail, zurück zur App → automatischer Refresh. U-Bahn offline, wieder 4G → automatisches Nachladen.
Als ich das zum ersten Mal als Standard sah, war ich überrascht. Früher: entweder veraltete Daten oder manuell visibilitychange, online – viel Code. Jetzt: gratis.
Das Key-Konzept:
Der erste Parameter von useSWR ist ein String wie '/api/users' – der Key, zentral wichtig.
// Zwei Komponenten, gleicher Key
function Header() {
const { data } = useSWR('/api/user', fetcher);
return <div>Willkommen, {data?.name}</div>;
}
function Profile() {
const { data } = useSWR('/api/user', fetcher);
return <div>Profil: {data?.email}</div>;
}
Gleicher Key → geteilte Daten. Ein API-Call, beide Komponenten synchron.
Kein State-Management, kein Context – nur der Key.
Cache-Strategien im Detail
SWR cached und revalidiert automatisch – in der Praxis brauchen unterschiedliche Daten unterschiedliche „Frische“. Avatar ändert sich selten, Aktienkurse sekündlich. Dafür passen Sie die Strategie an.
Standard ist smart, aber nicht universal
SWR ist standardmäßig recht aggressiv:
- Revalidierung bei jedem Mount
- Revalidierung bei Fokus
- Revalidierung bei Reconnect
Perfekt für Echtzeit (Chat, Online-Status). Für stabile Daten (Artikelliste, Profil) oft zu viel – Tab hin und her, jedes Mal Request? Unnötig.
Wichtige Optionen
const { data } = useSWR('/api/articles', fetcher, {
revalidateOnFocus: false, // kein Request bei Fokus
revalidateOnReconnect: false, // kein Request bei Reconnect
refreshInterval: 0, // Polling in ms, 0 = aus
dedupingInterval: 2000, // gleiche Requests innerhalb 2 s deduplizieren
});
Die Namen sind selbsterklärend.
Szenario 1: Echtzeit (Aktien, Online-Zähler)
const { data } = useSWR('/api/stock/AAPL', fetcher, {
refreshInterval: 1000, // jede Sekunde
revalidateOnFocus: true // beim Zurückkommen sofort aktualisieren
});
Maximale Aktualität – Polling ist simpel (WebSocket wäre besser, anderes Thema).
Szenario 2: Relativ stabil (Profil, Artikelliste)
const { data } = useSWR('/api/profile', fetcher, {
revalidateOnFocus: false,
refreshInterval: 0,
dedupingInterval: 60000, // 1 Minute keine Duplikate
});
Profil ändert sich selten – Tab-Wechsel ohne Refresh. Nach manueller Änderung können Sie mutate nutzen (später).
Szenario 3: Fast statisch (Docs, Hilfe)
const { data } = useSWR('/api/docs', fetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
revalidateOnMount: false,
revalidateIfStale: false,
});
Monatelang unverändert – nach erstem Load praktisch dauerhafter Cache bis manueller Reload.
Global vs. lokal
Einheitliche App-Strategie mit SWRConfig:
import { SWRConfig } from 'swr';
function App() {
return (
<SWRConfig value={{
refreshInterval: 3000,
fetcher: (url) => fetch(url).then(r => r.json()),
revalidateOnFocus: false,
}}>
<Dashboard />
</SWRConfig>
);
}
Alle Kind-useSWR erben die Werte; einzelne Hooks können überschreiben:
// revalidateOnFocus: true überschreibt globales false
const { data } = useSWR('/api/realtime', fetcher, {
revalidateOnFocus: true
});
Request-Deduplizierung
Drei Komponenten mounten gleichzeitig mit useSWR('/api/user') – SWR sendet einen Request, alle teilen das Ergebnis.
Standard-Fenster: 2 Sekunden (dedupingInterval: 2000). Bei schnellem Tab-Wechsel den Wert erhöhen.
Das hat mir API-Kontingent gerettet: schnelles Scrollen löste kurz hintereinander dieselben Requests aus – mit Deduplizierung sofort ruhiger. Symptom behoben, Ursache im Code trotzdem nachziehen.
Bedingtes Fetching
Erst A, dann B – Key null pausiert:
const { data: user } = useSWR('/api/user', fetcher);
const { data: projects } = useSWR(
user ? `/api/projects?userId=${user.id}` : null,
fetcher
);
Ohne user kein Request; sobald user da ist, startet projects. Serielle Abhängigkeit, minimaler Aufwand.
Optimistische Updates: bessere UX
Cache macht es schnell – bei Nutzeraktionen bleibt eine Frage: „Gefällt mir“ erst nach API-Antwort, oder sofort?
Optimistische Updates (Optimistic Update): Erfolg annehmen, UI sofort ändern, bei Fehler zurückrollen. Klingt riskant – die meisten Aktionen gelingen, der UX-Gewinn ist sichtbar.
Klassisch vs. optimistisch
Klassisch:
- Klick auf „Gefällt mir“
- Loading oder disabled
- 500 ms–2 s warten
- Herz erscheint
- Nutzer: „Langsam …“
Optimistisch:
- Klick
- Herz sofort
- Request im Hintergrund
- Meist Erfolg – nichts weiter
- Selten Fehler – Herz grau, Hinweis
Latenz gefühlt 0 ms.
mutate: Cache manuell steuern
import { mutate } from 'swr';
// Revalidierung für /api/user auslösen
mutate('/api/user');
Für Optimismus brauchen Sie mehr – Todo-Beispiel:
import useSWR, { mutate } from 'swr';
function TodoList() {
const { data: todos } = useSWR('/api/todos', fetcher);
const addTodo = async (text) => {
const newTodo = { id: Date.now(), text, completed: false };
mutate(
'/api/todos',
async (currentTodos) => {
const optimisticData = [...currentTodos, newTodo];
const savedTodo = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo)
}).then(r => r.json());
return [...currentTodos, savedTodo];
},
{
optimisticData: [...todos, newTodo],
rollbackOnError: true,
revalidate: false,
}
);
};
return (
<div>
{todos?.map(todo => <div key={todo.id}>{todo.text}</div>)}
<button onClick={() => addTodo('New task')}>Add</button>
</div>
);
}
Ablauf: Klick → sofort neues Todo (optimisticData) → POST → bei Erfolg Server-Daten (id, Timestamps) → bei Fehler Rollback (rollbackOnError: true).
Vier zentrale Optionen
1. optimisticData – sofort anzeigen
optimisticData: [...todos, newTodo]
Oder als Funktion:
optimisticData: (currentTodos) => [...currentTodos, newTodo]
2. populateCache – Rückgabewert in Cache?
Standard true. Server liefert oft id, createdAt – dann sinnvoll. Unvollständige API-Antwort → false.
3. revalidate – nochmal fetchen?
Meist false – Sie haben manuell aktualisiert. Nur wenn Sie SWR zur Bestätigung brauchen.
4. rollbackOnError – bei Fehler zurück?
Wichtig: Fehler in mutate → automatisch vorheriger Stand. Like-Herz leuchtet, API scheitert → Herz wieder aus.
Feiner steuern:
rollbackOnError: (error) => {
return error.name !== 'AbortError';
}
Praxis: Todo löschen
const deleteTodo = async (id) => {
mutate(
'/api/todos',
async (currentTodos) => {
const optimistic = currentTodos.filter(t => t.id !== id);
await fetch(`/api/todos/${id}`, { method: 'DELETE' });
return optimistic;
},
{
optimisticData: todos.filter(t => t.id !== id),
rollbackOnError: true,
}
);
};
Klick → Todo weg. Server-Fehler → Todo zurück, Fehlermeldung.
useSWRMutation – sauberer
Ab SWR 2.0:
import useSWRMutation from 'swr/mutation';
async function updateUser(url, { arg }) {
await fetch(url, {
method: 'POST',
body: JSON.stringify(arg)
});
}
function Profile() {
const { trigger, isMutating } = useSWRMutation('/api/user', updateUser);
return (
<button
onClick={() => trigger({ name: 'John' })}
disabled={isMutating}
>
Update Name
</button>
);
}
trigger startet die Mutation, isMutating ersetzt manuelles Loading.
Wann optimistisch?
✅ Geeignet:
- Like/Favorit (selten Fehler, reversibel)
- Liste hinzufügen/löschen
- Toggle (Benachrichtigungen)
- Entwurf speichern
❌ Ungeeignet:
- Zahlung (Bestätigung abwarten)
- Konto löschen (irreversibel)
- Passwort, Berechtigungen
- Server-generierte Ergebnisse (Report)
Regel: niedrige Fehlerrate, reversibel, UX zuerst → optimistisch. Kritisch oder irreversibel → auf Server warten.
Ein Fehler aus eigener Erfahrung: Kommentare mit Optimismus – langsames Netz, mehrfach „Senden“ geklickt, dutzende identische Einträge in der UI. Lösung: isMutating, Button während Request disabled. Optimismus ja – Randfälle mitdenken.
SWR vs. React Query: Auswahl
Beide sind stark und 2025 aktiv gepflegt. Falsche Wahl ist selten fatal – passende Wahl spart Aufwand.
Größe: SWR leichter
- SWR: 5,3 KB (gzip)
- React Query (TanStack Query): 16,2 KB
Bei strengem Bundle-Budget (Landingpage, Mobile H5) zählt SWR. In vielen Apps sind ~10 KB vernachlässigbar.
Komplexität: SWR einfacher
Kern-API: ein useSWR. In Minuten verstanden.
React Query: QueryClient, useQuery, useMutation, queryKeys, cache time vs. stale time – steilere Lernkurve, lange Docs.
Jüngere Teams → oft SWR.
Funktion: React Query umfangreicher
- Offizielle DevTools – Queries, Cache, Refetch sichtbar. SWR ohne offizielle DevTools.
- Feinere Cache-Kontrolle – cache time und stale time getrennt; SWR: Cache + Revalidate.
- Pagination/Infinite –
useInfiniteQueryvs.useSWRInfinite, React Query etwas ausgereifter. - Mutationen –
useMutationmit globalem State, Retry, Callbacks;useSWRMutationerst ab 2.0, schlanker.
Community
TanStack Query: größere Community, mehr Stack-Overflow-Antworten.
SWR: Vercel, enge Next.js-Integration – bei Next.js oft die naheliegende Wahl.
Empfehlung
| SWR | React Query |
|---|---|
| Einfache Datenlogik | Komplexe Cache-Anforderungen |
| Bundle sensibel | +10 KB ok |
| Next.js | CRA, Vite, andere |
| Wenig Frontend-Erfahrung | Team mag mächtige Tools |
| Schneller Start | Zeit für Ökosystem |
Eigene Praxis: MVP → SWR. Wächst die Komplexität → ggf. React Query. Meist reicht SWR.
Migration ist überschaubar – Cache, Revalidate, Mutation sind verwandt. Nicht zu lange grübeln.
Next.js und SWR
Beide von Vercel – Integration fällt leicht.
App Router
Server Components sind Standard; SWR ist Client-seitig:
'use client';
import useSWR from 'swr';
export default function Profile() {
const { data } = useSWR('/api/user', fetcher);
return <div>{data?.name}</div>;
}
'use client' nicht vergessen.
SSR + Fallback
Initialdaten aus getStaticProps / getServerSideProps als Fallback:
// pages/profile.js
export async function getStaticProps() {
const user = await fetch('https://api.example.com/user').then(r => r.json());
return {
props: {
fallback: {
'/api/user': user
}
},
revalidate: 60
}
}
export default function Profile({ fallback }) {
return (
<SWRConfig value={{ fallback }}>
<UserProfile />
</SWRConfig>
);
}
function UserProfile() {
const { data } = useSWR('/api/user', fetcher);
return <div>{data.name}</div>;
}
Erster Besuch: voller Inhalt (SEO), danach SWR-Revalidierung im Client.
Prefetch
import { mutate } from 'swr';
function ArticleLink({ id }) {
const prefetch = () => {
mutate(`/api/article/${id}`, fetch(`/api/article/${id}`).then(r => r.json()));
};
return (
<Link href={`/article/${id}`} onMouseEnter={prefetch}>
Read more
</Link>
);
}
Hover lädt schon – Klick oft aus dem Cache.
Infinite Scroll: useSWRInfinite
import useSWRInfinite from 'swr/infinite';
function ArticleList() {
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null;
return `/api/articles?page=${pageIndex + 1}&limit=10`;
};
const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher);
const articles = data ? data.flat() : [];
const isLoadingMore = isLoading || (size > 0 && data && typeof data[size - 1] === 'undefined');
return (
<div>
{articles.map(article => (
<div key={article.id}>{article.title}</div>
))}
<button
onClick={() => setSize(size + 1)}
disabled={isLoadingMore}
>
{isLoadingMore ? 'Loading...' : 'Load More'}
</button>
</div>
);
}
setSize(size + 1) lädt die nächste Seite; jede Seite bleibt gecacht.
Performance in Next.js
-
Revalidate-Frequenz
- Statisch: Auto-Revalidate aus
- Nutzerdaten: Fokus-Revalidate
- Echtzeit:
refreshInterval
-
API-Route-Cache
Cache-Controlauf API-Routen- SWR respektiert HTTP-Cache
-
Keine Request-Wasserfall
- Abhängige Daten ggf. serverseitig bündeln
- oder paralleles Fetching in Next.js
Praxis: Blog-Liste mit SWR + ISR – statisches HTML sofort, im Hintergrund neue Artikel. Schnell und SEO-tauglich.
Statische Performance plus dynamische Frische – das ist Next.js + SWR.
Fazit
SWR adressiert drei Probleme: Boilerplate, fehlender Cache, Sync über Komponenten. stale-while-revalidate ist schnell und aktuell. Revalidierung, Deduplizierung, Optimismus decken die meisten Fälle ab.
Best Practices:
- Cache je nach Datentyp – Echtzeit pollend, Stabiles länger cachen
- Optimismus bei reversiblen Aktionen – niedrige Fehlerrate voraussetzen
- Keys zum Teilen – kein Redux nur für geteilte API-Daten
- Next.js: Fallback für SSR
- Einfach → SWR, komplex → React Query – kein Over-Engineering
SWR ist kein Wundermittel – schlechte APIs und Architektur ersetzt es nicht. Bei soliden APIs wird Frontend-Code deutlich schlanker.
Im nächsten Projekt ausprobieren – Sie werden den Unterschied merken.
Vollständiger SWR-Workflow
Von Installation über Konfiguration und Nutzung bis zur Cache-Optimierung
⏱️ Estimated time: 1 hr
- 1
Step 1: Installation und Grundlagen
Installation:
```bash
npm install swr
```
Grundlegende Nutzung:
```tsx
'use client'
import useSWR from 'swr'
const fetcher = (url: string) => fetch(url).then(r => r.json())
export function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher)
if (error) return <div>Failed to load</div>
if (isLoading) return <div>Loading...</div>
return <div>{data.map(user => <div key={user.id}>{user.name}</div>)}</div>
}
```
Wichtig:
• Komponente mit 'use client' markieren
• fetcher für Datenabruf
• useSWR liefert data, error, isLoading - 2
Step 2: Globale Optionen konfigurieren
Mit SWRConfig:
```tsx
'use client'
import { SWRConfig } from 'swr'
const fetcher = (url: string) => fetch(url).then(r => r.json())
export function Providers({ children }) {
return (
<SWRConfig
value={{
fetcher,
revalidateOnFocus: true,
revalidateOnReconnect: true,
refreshInterval: 0,
}}
>
{children}
</SWRConfig>
)
}
```
Häufige Optionen:
• revalidateOnFocus: Revalidierung bei Fokus
• revalidateOnReconnect: Revalidierung bei Reconnect
• refreshInterval: Intervall (0 = aus)
• dedupingInterval: Deduplizierung (Standard 2000 ms)
Wichtig: Konfiguration in der Root – alle Kinder erben sie - 3
Step 3: Next.js-Integration (SSR)
Mit Fallback-Daten:
```tsx
// app/users/page.tsx (Server Component)
import { getUsers } from '@/lib/users'
export default async function UsersPage() {
const initialUsers = await getUsers()
return <UsersList initialUsers={initialUsers} />
}
// components/UsersList.tsx (Client Component)
'use client'
import useSWR from 'swr'
export function UsersList({ initialUsers }) {
const { data } = useSWR('/api/users', fetcher, {
fallbackData: initialUsers
})
return <div>{data.map(...)}</div>
}
```
Vorteile:
• Erster Screen aus SSR – schnell
• Updates über SWR – gute UX
• SSR plus Client-Cache kombiniert
Wichtig: fallbackData statt initialData (fallbackData löst keine Revalidierung aus) - 4
Step 4: Optimistische Updates
Mit mutate:
```tsx
import { mutate } from 'swr'
async function updateUser(id: string, name: string) {
mutate(`/api/users/${id}`, { ...user, name }, false)
await fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify({ name })
})
mutate(`/api/users/${id}`)
}
```
Wichtig:
• Dritter Parameter false = keine Revalidierung
• Nach Erfolg erneut mutate zur Bestätigung
• Nutzer sieht Update sofort
FAQ
Was ist SWR und warum braucht man es?
Schmerzpunkte klassisch:
• useState, useEffect, Loading, Error pro Request
• Mindestens 20 Zeilen
• Gemeinsame Daten: State-Lifting oder doppelte Requests
SWR-Vorteile:
• Automatischer Cache über gleiche Keys
• Revalidierung bei Fokus und Reconnect
• Request-Deduplizierung
• Fehler-Retry
• Ein Hook für bis zu 90 % weniger Code
Drei Zeilen für 80 % der Fälle:
```tsx
const { data, error, isLoading } = useSWR('/api/users', fetcher)
```
Was ist der Unterschied zwischen SWR und React Query?
• Schlank, niedrige Lernkurve
• Für die meisten Projekte
• Weniger Features
• Vercel, gute Next.js-Integration
React Query:
• Mächtiger, mehr Features
• Komplexe Szenarien
• Höhere Lernkurve
• Flexibler, mehr Konfiguration
Empfehlung:
• Einfaches Projekt → SWR
• Komplex → React Query
• Unsicher → SWR, bei Bedarf wechseln
Kein Over-Engineering – einfach SWR, komplex React Query.
Wie nutzt man SWR in Next.js?
```tsx
// app/users/page.tsx (Server Component)
export default async function UsersPage() {
const initialUsers = await getUsers()
return <UsersList initialUsers={initialUsers} />
}
// components/UsersList.tsx (Client Component)
'use client'
export function UsersList({ initialUsers }) {
const { data } = useSWR('/api/users', fetcher, {
fallbackData: initialUsers
})
return <div>{data.map(...)}</div>
}
```
Vorteile:
• Erster Screen aus SSR
• Client-Updates über SWR
• SSR und Client-Cache kombiniert
Wichtig:
• fallbackData statt initialData
• fallbackData triggert keine Revalidierung
• Gut für App Router
Wie funktioniert die SWR-Cache-Strategie?
Ablauf:
1. Erster Request: Loading
2. Erfolg: Anzeige und Cache
3. Erneuter Zugriff: Cache sofort (schnell)
4. Hintergrund: Revalidierung (aktuell)
5. Fertig: leises Update (nahtlos)
Eigenschaften:
• Cache über Keys
• Deduplizierung
• Revalidierung bei Fokus/Reconnect
• Fehler-Retry
Optionen:
• revalidateOnFocus
• revalidateOnReconnect
• refreshInterval
• dedupingInterval
Nutzer sehen selten Loading, Daten bleiben frisch.
Wie implementiert man optimistische Updates?
```tsx
import { mutate } from 'swr'
async function updateUser(id: string, name: string) {
mutate(`/api/users/${id}`, { ...user, name }, false)
await fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify({ name })
})
mutate(`/api/users/${id}`)
}
```
Wichtig:
• Dritter Parameter false = keine Revalidierung
• Nach Erfolg erneut mutate
• Sofort sichtbare UI-Änderung
Bei Fehler: auf ursprüngliche Daten zurückrollen.
Für welche Szenarien eignet sich SWR?
• Häufig aktualisierte Daten (Listen, Benachrichtigungen)
• Mehrere Komponenten, dieselben Daten
• Automatischer Cache und Revalidierung
• Next.js-Projekte
Weniger geeignet:
• Einmalige statische Daten
• Nur ein Request ohne Revalidierung
• Sehr komplexe Cache-Anforderungen → React Query
Empfehlung:
• Meiste Projekte → SWR
• Komplex → React Query
• Sehr einfach → fetch
SWR nicht um der Bibliothek willen – nach Bedarf wählen.
11 Min. Lesezeit · Veröffentlicht am: 19. Dez. 2025 · Aktualisiert am: 14. Juli 2026
Next.js Komplettleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
JWT oder Session? Nach diesem Guide ist die Entscheidung klar
JWT oder Session? Praxisvergleich beider Sitzungsstrategien: NextAuth.js-Konfiguration, Performance, Sicherheit – damit Sie die richtige Wahl treffen.
Teil 49 von 51
Nächster
Drizzle ORM Praxisleitfaden: 90 % leichter als Prisma – die TypeScript-ORM-Wahl
Drizzle ORM wiegt nur 7,4 kb – 90 % leichter als Prisma, 73 % schnellerer Cold Start. Next.js + Drizzle Setup, SQL-like API und Performance-Vergleich mit Prisma für die passende TypeScript-ORM-Entscheidung.
Teil 51 von 51
Ähnliche Beiträge
Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick

Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick
Next.js 15 in der Praxis: Wie ich an einem Wochenende ein produktionsreifes Blog-System aufgebaut habe


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen