Retour aux articles
10 Février 2026

Next.js 16 : SSR, SSG et ISR expliqués simplement

Next.js 16 : SSR, SSG et ISR expliqués simplement
Next.js

Pourquoi plusieurs stratégies de rendu ?

Quand un visiteur arrive sur ton site, il faut bien qu'à un moment, le HTML soit généré. La question que Next.js pose est simple : quand ce HTML doit-il être fabriqué ?

  • Une fois pour toutes, à l'avance ?
  • À chaque visite, en temps réel ?
  • Un mélange des deux, avec une mise à jour périodique ?

C'est exactement ce que couvrent SSG, SSR et ISR. Ce ne sont pas des concepts concurrents — ce sont trois outils dans la même boîte, et le bon choix dépend entièrement de la nature de la page.

Analogie simple : imagine un restaurant. SSG, c'est le buffet préparé le matin — rapide à servir, mais figé. SSR, c'est la cuisine à la commande — toujours frais, mais plus lent. ISR, c'est le buffet réapprovisionné toutes les 30 minutes — rapide ET raisonnablement frais.

SSG — Static Site Generation

Avec le SSG, Next.js génère le HTML de la page une seule fois, au moment du build (npm run build). Ce HTML est ensuite servi tel quel à tous les visiteurs, depuis un CDN si besoin — aucun calcul serveur à chaque visite.

Résultat : c'est la stratégie la plus rapide possible. Le fichier HTML existe déjà, prêt à être envoyé.

Exemple de code

tsx
// app/projets/page.tsx
// Pas de fetch dynamique, pas de cookies, pas de searchParams
// → Next.js génère cette page en statique par défaut

import { projects } from "@/app/constant/Data";

export default function Projets() {
	return (
		<ul>
			{projects.map((p) => (
				<li key={p.slug}>{p.title}</li>
			))}
		</ul>
	);
}

Pour une route dynamique comme /projets/[slug], il faut indiquer à Next.js quelles pages générer à l'avance avec generateStaticParams :

tsx
// app/projets/[slug]/page.tsx
export async function generateStaticParams() {
	return projects.map((project) => ({ slug: project.slug }));
}

export default async function ProjectDetail({ params }) {
	const { slug } = await params;
	const project = projects.find((p) => p.slug === slug);
	// Cette page est pré-générée pour CHAQUE slug listé ci-dessus
}

Quand utiliser le SSG

Contenu identique pour tous les visiteurs et qui change rarement : pages "À propos", articles de blog, portfolio de projets, documentation, pages marketing.

SSR — Server-Side Rendering

Avec le SSR, Next.js génère le HTML à chaque requête, sur le serveur, au moment précis où l'utilisateur visite la page. Rien n'est pré-calculé à l'avance.

Résultat : le contenu est toujours à jour à la seconde près, mais chaque visite déclenche un vrai travail serveur — donc un temps de réponse plus long qu'en SSG.

Exemple de code

tsx
// app/dashboard/page.tsx
import { cookies } from "next/headers";

export default async function Dashboard() {
	const cookieStore = await cookies();
	const userId = cookieStore.get("userId")?.value;

	// La présence de cookies() force Next.js à passer en SSR :
	// impossible de pré-générer une page qui dépend d'un utilisateur précis
	const res = await fetch(`https://api.exemple.com/user/${userId}/stats`, {
		cache: "no-store", // force une requête fraîche à chaque fois
	});
	const stats = await res.json();

	return <div>Bienvenue, voici tes stats : {stats.total}</div>;
}

L'option cache: "no-store" est la clé : elle dit explicitly à Next.js de ne jamais réutiliser une réponse mise en cache et de tout recalculer à chaque requête.

Quand utiliser le SSR

Contenu personnalisé par utilisateur ou qui doit être garanti frais à 100% : tableau de bord connecté, panier d'achat, résultats de recherche, cours de bourse en direct.

ISR — Incremental Static Regeneration

L'ISR est le compromis entre les deux. La page est générée statiquement comme en SSG, mais Next.js la régénère automatiquement en arrière-plan après un délai que tu définis — sans jamais faire attendre un visiteur.

Comment ça marche concrètement : le premier visiteur après l'expiration du délai reçoit quand même l'ancienne version (rendu instantané), mais Next.js déclenche en parallèle un recalcul silencieux. Les visiteurs suivants recevront la version fraîche.

Exemple de code

tsx
// app/articles/page.tsx
export default async function Articles() {
	const res = await fetch("https://api.exemple.com/articles", {
		next: { revalidate: 3600 }, // régénère au plus une fois par heure
	});
	const articles = await res.json();

	return (
		<div>
			{articles.map((a) => (
				<ArticleCard key={a.slug} {...a} />
			))}
		</div>
	);
}

Tu peux aussi définir le délai de revalidation pour toute une route avec une constante exportée :

tsx
// app/articles/page.tsx
export const revalidate = 3600; // secondes

export default async function Articles() {
	// ...
}

L'ISR ne s'applique qu'aux données récupérées via fetch côté serveur (ou aux fonctions fs comme lib/markdown.ts combinées à revalidate). Une donnée lue depuis cookies() ou searchParams force toujours du SSR, quel que soit le revalidate défini.

Quand utiliser l'ISR

Contenu qui change, mais pas à chaque seconde : liste d'articles de blog, catalogue produit e-commerce, page d'accueil avec compteur de vues, résultats sportifs mis à jour toutes les 5 minutes.

Comment Next.js choisit automatiquement

Avec l'App Router, tu n'as presque jamais besoin de déclarer explicitement "cette page est en SSR" ou "cette page est en SSG". Next.js décide automatiquement selon ce que ton code utilise :

tsx
// → SSG automatique : aucune donnée dynamique utilisée
export default function About() {
	return <p>Page statique, pas de fetch, pas de cookies.</p>;
}
tsx
// → SSR automatique : utilisation de cookies()
import { cookies } from "next/headers";

export default async function Profile() {
	const session = (await cookies()).get("session");
	return <p>Connecté : {session?.value}</p>;
}
tsx
// → ISR automatique : fetch avec revalidate
export default async function Blog() {
	const res = await fetch("https://api.exemple.com/posts", {
		next: { revalidate: 60 },
	});
	// ...
}

La règle mentale à retenir : dès que Next.js détecte une source de données qui varie par requête (cookies(), headers(), searchParams, ou un fetch avec cache: "no-store"), il bascule automatiquement en SSR pour cette page. Sinon, il opte pour le SSG — et si un revalidate est présent sur un fetch, ce SSG devient de l'ISR.

Tableau comparatif

SSG SSR ISR
Quand le HTML est généré Une fois, au build À chaque requête Au build, puis périodiquement
Vitesse perçue ⚡️ Très rapide 🐢 Dépend du serveur ⚡️ Très rapide
Fraîcheur des données Figée jusqu'au prochain build Toujours à jour À jour avec un délai maîtrisé
Charge serveur Aucune (fichier statique) Une requête = un calcul Minime (recalcul occasionnel)
Cas d'usage type Page "À propos", CGU Dashboard utilisateur Blog, catalogue produits
Déclencheur dans le code Aucun fetch dynamique cookies(), no-store fetch + revalidate

Comment tester toi-même

La meilleure façon de comprendre la différence, c'est de la voir en pratique. Voici un mini test à faire sur ton propre projet.

Étape 1 — Crée une route qui affiche l'heure de génération :

tsx
// app/test-rendering/page.tsx
export const revalidate = 10; // ISR toutes les 10 secondes

export default function TestRendering() {
	const generatedAt = new Date().toLocaleTimeString("fr-FR");
	return (
		<div>
			<h1>Page générée à : {generatedAt}</h1>
			<p>
				Rafraîchis la page plusieurs fois d'affilée pour observer le
				comportement.
			</p>
		</div>
	);
}

Étape 2 — Build et lance en production (le comportement de cache ne s'observe pas correctement en next dev) :

bash
npm run build
npm run start

Étape 3 — Ouvre http://localhost:3000/test-rendering et rafraîchis plusieurs fois dans la même seconde : l'heure affichée ne change pas. Attends 10 secondes, rafraîchis à nouveau : l'heure change enfin — c'est l'ISR en action, la régénération n'a lieu qu'après le délai défini.

Pour observer du SSR pur, remplace export const revalidate = 10 par export const dynamic = "force-dynamic" : l'heure changera alors à chaque rafraîchissement, sans exception.

En résumé

Concept À retenir
SSG HTML figé au build, ultra-rapide, pour du contenu qui ne bouge pas
SSR HTML recalculé à chaque requête, pour du contenu personnalisé ou temps réel
ISR Le meilleur des deux mondes : statique, mais qui se rafraîchit tout seul
Choix automatique Next.js détecte cookies(), no-store ou revalidate et adapte la stratégie sans configuration explicite
Test local npm run build && npm run start, jamais next dev, pour observer le vrai comportement de cache

Il n'y a pas de stratégie "meilleure" dans l'absolu — le bon réflexe est de se poser la question par page, voire par section de page : est-ce que ce contenu est identique pour tout le monde ? Change-t-il souvent ? Peut-il tolérer quelques secondes ou minutes de retard ? Les réponses à ces trois questions suffisent à choisir entre SSG, SSR et ISR.