Retour au cours

frontend / nextjs

Rendu SSG, SSR et ISR

Leçon 101 exercice

Explication

Ce que vous allez apprendre

  • Distinguer SSG, SSR et ISR et savoir quand choisir chacune de ces stratégies
  • Forcer explicitement un mode avec dynamic et revalidate au niveau d'une route entière
  • Comprendre pourquoi ce choix se fait route par route, pas pour toute l'application
  • Mettre en place une régénération "à la demande" déclenchée par un webhook plutôt qu'un timer
  • Repérer les signaux (fetch, cookies, headers) qui font basculer Next.js vers un mode dynamique automatiquement

Dans quel contexte ?

Une même application contient une page "À propos" qui ne change jamais, un tableau de bord /dashboard affichant des métriques en temps réel propres à chaque utilisateur connecté, et un catalogue produit /produits mis à jour quelques fois par jour par l'équipe marketing. Appliquer la même stratégie de rendu aux trois serait une erreur : la première n'a besoin que du SSG, la deuxième exige du SSR, et la troisième profite pleinement de l'ISR. Cette leçon donne les critères pour choisir correctement, route par route.

Formalisons ce qu'on a déjà entrevu

Les leçons précédentes sur le cache ont déjà montré des indices : Next.js propose trois grandes stratégies de rendu. Chacune est adaptée à un type de contenu différent.

Commençons par la plus simple : le SSG (Static Site Generation). La page est générée UNE FOIS, au moment du build, puis servie telle quelle à tous les visiteurs, comme un fichier statique. C'est le plus rapide, mais adapté seulement à du contenu qui ne change pas souvent — une page "À propos", un article déjà publié.

À l'opposé, il y a le SSR (Server-Side Rendering). La page est recalculée à CHAQUE requête. Indispensable pour du contenu personnalisé (un tableau de bord en temps réel, du contenu propre à l'utilisateur connecté), mais plus coûteux en ressources serveur.

Entre les deux, un compromis intéressant : l'ISR (Incremental Static Regeneration). La page est servie depuis un cache statique (rapide comme du SSG), mais régénérée en arrière-plan après un délai (revalidate), sans jamais faire attendre l'utilisateur pour cette régénération.

StratégieRapidité perçueFraîcheur des donnéesExemple
SSGMaximaleFixée au buildPage "À propos", CGU
SSRCorrecteToujours à jourDashboard temps réel, contenu par utilisateur
ISRMaximaleRafraîchie périodiquementCatalogue produit, blog

Prérequis

Cette leçon suppose que tu es à l'aise avec les options cache et revalidate de fetch, vues dans la leçon sur le cache et la revalidation : ici, on les applique au niveau d'une route entière.

Pourquoi ce choix est stratégique et pas juste technique ? Du SSR partout gaspille des ressources serveur sur du contenu qui ne change jamais. Du SSG partout peut, à l'inverse, afficher des données périmées pendant des heures.

Il existe une version encore plus fine de l'ISR : le "à la demande". Déclenchée par un webhook plutôt que par un timer, elle convient très bien à du contenu semi-dynamique comme un catalogue produit — les données restent fraîches SEULEMENT quand elles changent réellement.

Le piège fréquent à éviter : croire qu'il faut choisir UNE seule stratégie pour toute l'application. En réalité, ce choix se fait route par route, voire fetch par fetch : une page marketing peut être en SSG pendant qu'un dashboard est en SSR, dans la même application.

Bonne pratique

Commence toujours par te poser une question simple sur une nouvelle route : "cette page affiche-t-elle la même chose pour tout le monde ?". Si oui, SSG ou ISR conviennent. Si le contenu dépend de la session de l'utilisateur connecté, SSR est presque toujours nécessaire.

Un dernier point utile à savoir : Next.js déduit souvent automatiquement le bon mode selon l'usage de fetch, cookies ou headers dans le code — mais comprendre ce qui se passe reste essentiel pour ne pas être surpris par ce comportement par défaut.

Commandes & code

Rendu SSG, SSR et ISR

tsx
// SSG (Static Site Generation) — généré au build, servi comme fichier statique
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch("https://api.example.com/posts").then((r) => r.json());
  return posts.map((p: { slug: string }) => ({ slug: p.slug }));
}

export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await fetch(`https://api.example.com/posts/${slug}`, {
    cache: "force-cache", // SSG : capturé au build
  }).then((r) => r.json());

  return <Article post={post} />;
}
tsx
// SSR (Server-Side Rendering) — recalculé à CHAQUE requête
export const dynamic = "force-dynamic";

export default async function LiveDashboardPage() {
  const metrics = await fetch("https://api.example.com/metrics", {
    cache: "no-store",
  }).then((r) => r.json());

  return <MetricsPanel metrics={metrics} />;
}
tsx
// ISR (Incremental Static Regeneration) — le meilleur des deux mondes
// Sert une page statique, la régénère en arrière-plan après "revalidate" secondes
export const revalidate = 300; // régénération au plus toutes les 5 minutes

export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const product = await fetch(`https://api.example.com/products/${id}`, {
    next: { revalidate: 300 },
  }).then((r) => r.json());

  return <ProductDetails product={product} />;
}
tsx
// ISR à la demande (on-demand) : régénération déclenchée par événement, pas par timer
// app/api/webhooks/cms/route.ts
import { revalidatePath } from "next/cache";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const payload = await request.json();

  if (payload.event === "post.published") {
    revalidatePath(`/blog/${payload.slug}`);
    revalidatePath("/blog"); // invalide aussi la liste
  }

  return NextResponse.json({ ok: true });
}
StratégieQuandDonnées
SSGContenu identique pour tous, rarement modifiéGénérées au build
SSRContenu par requête (auth, temps réel)Générées à chaque requête
ISRContenu semi-statique (catalogue produits, blog)Régénérées périodiquement/à la demande

Résumé

  • Le mode par défaut d'une route est déduit automatiquement de son usage de fetch/cookies/headers.
  • force-static, force-dynamic et revalidate permettent de forcer explicitement une stratégie.
  • ISR à la demande (webhook + revalidatePath) évite le compromis "stale vs coût de calcul".

Exercices pratiques

1 disponible
1

Mission : trois pages, trois stratégies, un seul choix appliqué partout

Objectif : Attribuer la bonne stratégie de rendu (SSG, SSR, ISR) à trois routes aux besoins très différents.

Contexte

Un développeur pressé a mis export const dynamic = "force-dynamic" sur TOUTES les routes de l'application "pour être sûr que rien n'est jamais périmé" : la page /a-propos (contenu figé, jamais modifié), /dashboard (métriques temps réel propres à chaque utilisateur connecté) et /produits (catalogue mis à jour deux fois par jour par l'équipe marketing). Les temps de réponse et la facture serveur ont explosé.

Résoudre l’exercice →