Changer le thème

Guide complet SWR : maîtriser les stratégies de cache et les mises à jour optimistes en pratique

Easton editorial illustration: island architecture model

Vous fixez l’écran, le spinner de chargement familier — pour la Nième fois que vous rechargez la liste des utilisateurs. Chaque changement d’onglet : chargement. Chaque retour depuis une autre page : chargement. Même après un coup de fil qui fait perdre le focus au navigateur, au retour c’est encore chargement. Le pire ? Ces données ont été chargées il y a une seconde.

Dans un projet React, chaque récupération de données implique useState, useEffect, plus la gestion du loading et des erreurs. Vingt lignes de code minimum. Pire encore : plusieurs composants ont besoin des mêmes données — soit vous remontez l’état au parent (encore plus de code), soit chaque composant fait sa propre requête (gaspillage de bande passante).

Quand j’ai découvert SWR, la sensation était comparable à la première fois que j’ai utilisé Git à la place de copier des dossiers à la main. Trois lignes de code, 80 % des problèmes réglés. Sans exagérer.

Aujourd’hui, parlons de SWR — la bibliothèque React de récupération de données de Vercel. Le principe central tient en un mot : stale-while-revalidate (obsolète – revalider). Ça sonne technique ? L’idée est simple : d’abord vous voyez la « vieille photo » en cache (rapide), en arrière-plan on en prend une nouvelle (à jour), et une fois prête on la remplace discrètement (sans à-coup).

Pourquoi SWR ? Trois points douloureux du fetch classique

Regardons à quoi ressemble la récupération de données classique en React. Supposons que vous vouliez afficher une liste d’utilisateurs :

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>;
}

Vingt-quatre lignes de code — juste pour afficher une liste. Vous trouvez ça acceptable ? Pensez à ces scénarios :

Point douloureux 1 : du boilerplate à répétition

Chaque composant qui a besoin de données doit suivre le même schéma. Liste d’utilisateurs, liste d’articles, liste de commentaires — trois états, plein de if, gestion des erreurs… Le copier-coller devient embarrassant. Une fois, j’ai compté : dans un projet moyen, ce genre de snippets représentait environ 20 % du code.

Point douloureux 2 : le cache ? Inexistant

Le plus frustrant : pas de cache. L’utilisateur va de l’accueil à une page de détail, puis revient — encore un long chargement. Les données datent de trente secondes, et pourtant une nouvelle requête part. Les utilisateurs disent « votre site est lent » — souvent ce n’est pas l’API qui est lente, c’est l’absence totale de cache.

On peut construire une gestion d’état globale — Redux, Zustand, store, actions, reducers… Une fonction qui prendrait trois minutes devient trente minutes d’architecture.

Point douloureux 3 : synchroniser les données entre composants

Plusieurs composants, mêmes données : messages non lus dans l’en-tête, la barre latérale et la page des messages. Remonter l’état jusqu’en haut ? Des props sur dix niveaux. Context ? Chaque mise à jour re-rend tout l’arbre.

Une fois, j’ai passé deux jours sur une fonctionnalité de notification pourtant simple — au commit, le diff ne montrait que setState et useEffect. J’avais honte moi-même.

Voilà pourquoi SWR est populaire. Ce n’est pas un énième outil — il résout de vrais problèmes. Le même code avec 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>;
}

Neuf lignes. Terminé. Avec cache, revalidation automatique et partage entre composants. Le fetcher est votre fonction fetch — en général définie une fois globalement :

const fetcher = url => fetch(url).then(r => r.json());

La différence parle d’elle-même.

Concept central : la stratégie Stale-While-Revalidate

Le nom SWR vient de la stratégie de cache HTTP définie dans la RFC 5861. Pas de panique devant « RFC » — le principe est simple.

Analogie avec une photo : vous voulez une photo récente d’un ami. Méthode classique — l’appeler, attendre qu’il en prenne une et vous l’envoie. Avec SWR — d’abord sortir la vieille photo de votre dernière rencontre (peut-être la semaine dernière), la regarder en lui envoyant un message pour en prendre une nouvelle, puis remplacer quand elle arrive.

La différence : vous n’attendez pas les mains vides. Contenu immédiat (peut-être un peu daté), tout en garantissant la fraîcheur finale. C’est stale-while-revalidate — montrer l’obsolète, revalider en parallèle.

Flux complet de SWR :

  1. Étape 1 : retourner le cache immédiatement (stale)

    • Au montage, SWR vérifie le cache local
    • Cache présent ? Retour immédiat, page instantanée
    • Pas de cache ? undefined, affichage du loading
  2. Étape 2 : requête en arrière-plan (revalidate)

    • Avec ou sans cache : requête API
    • L’utilisateur ne le remarque pas, le contenu est déjà là
  3. Étape 3 : mise à jour des données

    • Après la réponse : cache et UI mis à jour discrètement
    • Si les données changent : React re-rend
    • Si rien ne change : rien ne se passe

Exemple concret — affichage d’un cours boursier :

function StockPrice({ symbol }) {
  const { data, error } = useSWR(`/api/stock/${symbol}`, fetcher);

  return (
    <div>
      <h2>{symbol}</h2>
      <p>Prix : {data ? `$${data.price}` : 'Loading...'}</p>
      <span>Mis à jour : {data?.updatedAt}</span>
    </div>
  );
}

À la première ouverture, data est undefined — « Loading… ». Une seconde plus tard, le prix s’affiche.

Changement d’onglet, dix minutes de mails, retour — prix instantané depuis le cache, pas de flash de loading. En parallèle, SWR lance une nouvelle requête. Cours modifié → UI mise à jour ; inchangé → ça reste.

Trois moments de revalidation automatique par défaut :

  1. Composant remonté (revalidateOnMount) : rechargement ou retour de route
  2. Fenêtre de nouveau au focus (revalidateOnFocus) : retour à l’onglet du navigateur
  3. Réseau rétabli (revalidateOnReconnect) : après une coupure

Vous n’avez rien à configurer — SWR garde les données fraîches. Une demi-heure de mails, retour sur votre site → refresh automatique. Métro sans réseau, retour en 4G → rechargement automatique.

Quand j’ai découvert que c’était le comportement par défaut, j’étais surpris. Avant : soit des données périmées, soit écouter manuellement visibilitychange, online — beaucoup de code. Maintenant : offert.

Le concept de key :

Le premier paramètre de useSWR est une chaîne comme '/api/users' — la key, centrale.

// Deux composants, même key
function Header() {
  const { data } = useSWR('/api/user', fetcher);
  return <div>Bienvenue, {data?.name}</div>;
}

function Profile() {
  const { data } = useSWR('/api/user', fetcher);
  return <div>Profil : {data?.email}</div>;
}

Même key → données partagées. Un seul appel API, les deux composants synchronisés.

Pas de gestion d’état, pas de Context — juste la key.

Stratégies de cache en détail : rendre le fetch plus intelligent

SWR met en cache et revalide automatiquement — en pratique, différentes données exigent une « fraîcheur » différente. L’avatar change rarement, le cours boursier chaque seconde. Il faut ajuster la stratégie.

Le comportement par défaut est malin, mais pas universel

SWR est assez agressif par défaut :

  • Revalidation à chaque montage
  • Revalidation au focus
  • Revalidation à la reconnexion

Parfait pour le temps réel (chat, statut en ligne). Pour des données stables (liste d’articles, profil), c’est parfois excessif — onglet aller-retour, requête à chaque fois ? Inutile.

Options de configuration essentielles

const { data } = useSWR('/api/articles', fetcher, {
  revalidateOnFocus: false,      // pas de requête au focus
  revalidateOnReconnect: false,  // pas de requête à la reconnexion
  refreshInterval: 0,            // intervalle de polling en ms, 0 = désactivé
  dedupingInterval: 2000,        // dédupliquer les requêtes identiques sur 2 s
});

Les noms sont explicites.

Scénario 1 : données temps réel (actions, compteur en ligne)

const { data } = useSWR('/api/stock/AAPL', fetcher, {
  refreshInterval: 1000,  // polling chaque seconde
  revalidateOnFocus: true // mise à jour immédiate au retour
});

Fraîcheur maximale — le polling est simple (WebSocket serait mieux, autre sujet).

Scénario 2 : données relativement stables (profil, liste d’articles)

const { data } = useSWR('/api/profile', fetcher, {
  revalidateOnFocus: false,
  refreshInterval: 0,
  dedupingInterval: 60000,      // pas de doublon pendant 1 minute
});

Le profil change rarement — changement d’onglet sans refresh. Après une modification manuelle, vous pouvez déclencher une mise à jour avec mutate (plus loin).

Scénario 3 : contenu quasi statique (documentation, aide)

const { data } = useSWR('/api/docs', fetcher, {
  revalidateOnFocus: false,
  revalidateOnReconnect: false,
  revalidateOnMount: false,
  revalidateIfStale: false,
});

Données qui peuvent rester un mois sans changer — après le premier chargement, cache quasi permanent jusqu’au rechargement manuel.

Configuration globale vs locale

Stratégie uniforme dans toute l’app avec SWRConfig :

import { SWRConfig } from 'swr';

function App() {
  return (
    <SWRConfig value={{
      refreshInterval: 3000,
      fetcher: (url) => fetch(url).then(r => r.json()),
      revalidateOnFocus: false,
    }}>
      <Dashboard />
    </SWRConfig>
  );
}

Tous les useSWR enfants héritent ces valeurs ; un Hook peut surcharger :

// revalidateOnFocus: true écrase le false global
const { data } = useSWR('/api/realtime', fetcher, {
  revalidateOnFocus: true
});

Déduplication des requêtes : économiser votre quota API

Trois composants montent en même temps avec useSWR('/api/user') — SWR n’envoie qu’une requête, tous partagent le résultat.

Fenêtre par défaut : 2 secondes (dedupingInterval: 2000). Si vos composants se remontent vite (changement d’onglet rapide), augmentez cette valeur.

Cette fonctionnalité m’a sauvé un quota API : un défilement rapide déclenchait la même requête en rafale — avec la déduplication, calme immédiat. Symptôme corrigé, mais corrigez aussi la cause dans le code.

Fetch conditionnel : requêtes dépendantes

D’abord A, puis B — une key à null met en pause :

const { data: user } = useSWR('/api/user', fetcher);

const { data: projects } = useSWR(
  user ? `/api/projects?userId=${user.id}` : null,
  fetcher
);

Sans user, pas de requête ; dès que user est là, projects démarre. Dépendance séquentielle, effort minimal.

Mises à jour optimistes : l’arme secrète pour une meilleure UX

Le cache règle la vitesse — pour les actions utilisateur, une question reste : afficher le cœur « J’aime » après la réponse API, ou immédiatement ?

Mise à jour optimiste (Optimistic Update) : supposer le succès, mettre à jour l’UI tout de suite, revenir en arrière en cas d’échec. Ça semble risqué — la plupart des actions réussissent, le gain UX est visible.

Classique vs optimiste

Classique :

  1. Clic sur « J’aime »
  2. Bouton en loading ou désactivé
  3. Attente 500 ms–2 s
  4. Cœur qui s’allume
  5. Utilisateur : « Ce site est lent… »

Optimiste :

  1. Clic
  2. Cœur immédiat
  3. Requête en arrière-plan
  4. Souvent succès — rien de plus
  5. Parfois échec — cœur gris, message d’erreur

Latence ressentie : 0 ms.

mutate : contrôler le cache manuellement

import { mutate } from 'swr';

// Déclencher la revalidation de /api/user
mutate('/api/user');

Pour l’optimisme, il faut plus — exemple Todo complet :

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>
  );
}

Flux : clic → nouveau Todo immédiat (optimisticData) → POST → succès : données serveur (id, timestamps) → échec : rollback (rollbackOnError: true).

Quatre options centrales

1. optimisticData — affichage immédiat

optimisticData: [...todos, newTodo]

Ou sous forme de fonction :

optimisticData: (currentTodos) => [...currentTodos, newTodo]

2. populateCache — mettre à jour le cache avec la valeur de retour ?

Par défaut true. Le serveur ajoute souvent id, createdAt — alors utile. Réponse API incomplète → false.

3. revalidate — refetch ?

En général false — vous avez mis à jour manuellement. Seulement si vous voulez que SWR confirme.

4. rollbackOnError — revenir en arrière en cas d’erreur ?

Important : erreur dans mutate → retour automatique à l’état précédent. Cœur allumé, API en échec → cœur éteint.

Contrôle plus fin :

rollbackOnError: (error) => {
  return error.name !== 'AbortError';
}

Pratique : supprimer un Todo

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,
    }
  );
};

Clic → Todo disparaît. Erreur serveur → Todo revient, message d’erreur.

useSWRMutation — une syntaxe plus élégante

Depuis 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 lance la mutation, isMutating remplace le loading manuel.

Quand utiliser l’optimisme ?

Adapté :

  • Like/favori (peu d’échecs, réversible)
  • Ajout/suppression dans une liste
  • Toggle (notifications)
  • Sauvegarde de brouillon

Inadapté :

  • Paiement (attendre la confirmation)
  • Suppression de compte (irréversible)
  • Mot de passe, permissions
  • Résultats calculés côté serveur (rapport)

Règle : faible taux d’échec, réversible, UX prioritaire → optimiste. Critique ou irréversible → attendre le serveur.

Une erreur vécue : commentaires avec optimisme — réseau lent, l’utilisateur clique « Envoyer » plusieurs fois, dizaines d’entrées identiques dans l’UI. Solution : isMutating, bouton désactivé pendant la requête. Optimisme oui — mais prévoir les cas limites.

SWR vs React Query : comment choisir ?

Les deux sont excellents et activement maintenus en 2025. Un mauvais choix est rarement fatal — le bon choix simplifie le développement.

Taille : SWR plus léger

  • SWR : 5,3 Ko (gzip)
  • React Query (TanStack Query) : 16,2 Ko

Avec un budget bundle strict (landing page, H5 mobile), SWR compte. Dans beaucoup d’apps, ~10 Ko sont négligeables.

Complexité : SWR plus simple

API centrale : un useSWR. Compris en quelques minutes.

React Query : QueryClient, useQuery, useMutation, queryKeys, cache time vs stale time — courbe d’apprentissage plus raide, documentation longue.

Équipes moins expérimentées → souvent SWR.

Fonctionnalités : React Query plus complet

  1. DevTools officielles — queries, cache, refetch visibles. SWR sans DevTools officielles.
  2. Contrôle de cache plus fin — cache time et stale time séparés ; SWR : cache + revalidate.
  3. Pagination/infiniteuseInfiniteQuery vs useSWRInfinite, React Query un peu plus mature.
  4. MutationsuseMutation avec état global, retry, callbacks ; useSWRMutation depuis 2.0, plus léger.

Communauté

TanStack Query : communauté plus large, plus de réponses sur Stack Overflow.

SWR : Vercel, intégration Next.js étroite — avec Next.js, choix naturel.

Recommandation

SWRReact Query
Logique de données simpleBesoins de cache complexes
Bundle sensible+10 Ko acceptable
Next.jsCRA, Vite, autres
Peu d’expérience frontendÉquipe qui aime les outils riches
Démarrage rapideTemps pour l’écosystème

Mon expérience : MVP → SWR. Complexité croissante → éventuellement React Query. La plupart du temps, SWR suffit.

La migration reste raisonnable — cache, revalidate, mutation sont proches. Ne sur-analysez pas.

Next.js et SWR : une combinaison naturelle

Tous deux de Vercel — l’intégration est fluide.

App Router

Les Server Components sont la norme ; SWR est côté client :

'use client';

import useSWR from 'swr';

export default function Profile() {
  const { data } = useSWR('/api/user', fetcher);
  return <div>{data?.name}</div>;
}

N’oubliez pas 'use client'.

SSR + fallback

Données initiales depuis getStaticProps / getServerSideProps comme 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>;
}

Première visite : contenu complet (SEO), puis revalidation SWR côté 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>
  );
}

Le survol charge déjà — le clic sort souvent du cache.

Défilement infini : 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) charge la page suivante ; chaque page reste en cache.

Performance avec Next.js

  1. Fréquence de revalidation

    • Contenu statique : désactiver la revalidation auto
    • Données utilisateur : garder la revalidation au focus
    • Temps réel : refreshInterval
  2. Cache des routes API

    • Cache-Control sur les routes API
    • SWR respecte les en-têtes HTTP de cache
  3. Éviter les cascades de requêtes

    • Données dépendantes : fusionner côté serveur si possible
    • Ou fetch parallèle dans Next.js

Cas réel : blog avec SWR + ISR — HTML statique instantané, en arrière-plan vérification des nouveaux articles. Rapide et SEO-friendly.

Performance statique plus fraîcheur dynamique — c’est Next.js + SWR.

Conclusion

SWR adresse trois problèmes : boilerplate, absence de cache, synchronisation entre composants. stale-while-revalidate est rapide et à jour. Revalidation, déduplication, optimisme couvrent la plupart des cas.

Bonnes pratiques :

  1. Adapter le cache au type de données — temps réel en polling, stable en cache long
  2. Optimisme pour les actions réversibles — faible taux d’échec requis
  3. Partager via les keys — pas de Redux juste pour des données API partagées
  4. Next.js : fallback pour le SSR
  5. Simple → SWR, complexe → React Query — pas de sur-ingénierie

SWR n’est pas une baguette magique — il ne corrige pas une mauvaise API ni une mauvaise architecture. Avec une API solide, le code frontend devient nettement plus élégant.

Essayez SWR sur votre prochain projet — vous verrez la différence.

Workflow complet SWR

De l'installation à la configuration, l'utilisation et l'optimisation du cache

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: Installation et bases

    Installation :
    ```bash
    npm install swr
    ```

    Utilisation de base :
    ```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>
    }
    ```

    Points clés :
    • Marquer la composante avec 'use client'
    • fetcher pour la récupération de données
    • useSWR retourne data, error, isLoading
  2. 2

    Step 2: Configurer les options globales

    Avec 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>
    )
    }
    ```

    Options courantes :
    • revalidateOnFocus : revalidation au focus de la fenêtre
    • revalidateOnReconnect : revalidation à la reconnexion réseau
    • refreshInterval : rafraîchissement périodique (0 = désactivé)
    • dedupingInterval : intervalle de déduplication (2000 ms par défaut)

    Point clé : configurer à la racine — tous les composants enfants héritent
  3. 3

    Step 3: Intégration Next.js (SSR)

    Avec fallback data :
    ```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>
    }
    ```

    Avantages :
    • Premier écran depuis le SSR — chargement rapide
    • Mises à jour via SWR — bonne UX
    • SSR et cache client combinés

    Point clé : utiliser fallbackData et non initialData (fallbackData ne déclenche pas de revalidation)
  4. 4

    Step 4: Mises à jour optimistes

    Avec 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}`)
    }
    ```

    Points clés :
    • Le troisième paramètre false = pas de revalidation
    • Après succès, mutate à nouveau pour confirmer
    • L'utilisateur voit la mise à jour immédiatement

FAQ

Qu'est-ce que SWR et pourquoi en avez-vous besoin ?
SWR est la bibliothèque React de Vercel pour la récupération de données ; le cœur est stale-while-revalidate.

Points douloureux du fetch classique :
• useState, useEffect, loading et error par requête
• Vingt lignes minimum
• Données partagées : remontée d'état ou requêtes dupliquées

Avantages de SWR :
• Cache automatique via les mêmes keys
• Revalidation au focus et à la reconnexion
• Déduplication des requêtes
• Retry automatique en cas d'erreur
• Un Hook pour simplifier jusqu'à 90 % du code

Trois lignes pour 80 % des cas :
```tsx
const { data, error, isLoading } = useSWR('/api/users', fetcher)
```
Quelle est la différence entre SWR et React Query ?
SWR :
• Léger, faible courbe d'apprentissage
• Convient à la plupart des projets
• Moins de fonctionnalités
• Vercel, bonne intégration Next.js

React Query :
• Plus puissant, plus de fonctionnalités
• Scénarios complexes
• Courbe d'apprentissage plus élevée
• Plus flexible, plus de configuration

Recommandation :
• Projet simple → SWR
• Complexe → React Query
• Incertain → SWR d'abord, migrer si besoin

Point clé : pas de sur-ingénierie — simple avec SWR, complexe avec React Query.
Comment utiliser SWR avec Next.js ?
Avec fallbackData pour le SSR :
```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>
}
```

Avantages :
• Premier écran depuis le SSR
• Mises à jour client via SWR
• SSR et cache client combinés

Points clés :
• fallbackData et non initialData
• fallbackData ne déclenche pas de revalidation
• Adapté à l'App Router
Quelle est la stratégie de cache de SWR ?
Cœur : stale-while-revalidate

Flux :
1. Première requête : loading
2. Succès : affichage et mise en cache
3. Accès suivant : cache immédiat (rapide)
4. Arrière-plan : revalidation (à jour)
5. Terminé : mise à jour discrète (sans à-coup)

Caractéristiques :
• Cache partagé via les keys
• Déduplication
• Revalidation au focus/reconnexion
• Retry en cas d'erreur

Options :
• revalidateOnFocus
• revalidateOnReconnect
• refreshInterval
• dedupingInterval

L'utilisateur voit rarement le loading, les données restent fraîches.
Comment implémenter les mises à jour optimistes ?
Avec 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}`)
}
```

Points clés :
• Troisième paramètre false = pas de revalidation
• Après succès, mutate à nouveau
• Mise à jour UI immédiate

En cas d'échec : revenir aux données d'origine.
Pour quels scénarios SWR est-il adapté ?
Adapté :
• Données fréquemment mises à jour (listes, notifications)
• Plusieurs composants, mêmes données
• Cache et revalidation automatiques
• Projets Next.js

Moins adapté :
• Données statiques uniques
• Une seule requête sans revalidation
• Besoins de cache très complexes → React Query

Recommandation :
• La plupart des projets → SWR
• Complexe → React Query
• Très simple → fetch direct

Point clé : n'utilisez pas SWR pour SWR — choisissez selon le besoin réel.

14 min de lecture · Publié le: 19 déc. 2025 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog