Picture of the author
Published on

Comprendre le rendu dans Next.js

Authors
Next.js, le framework React de pointe, offre une multitude de méthodes pour générer et afficher vos pages web. Mais qu’entend-on par « rendu » et pourquoi est-il essentiel de bien le comprendre ? Le rendu désigne le processus par lequel votre application génère le contenu visible par l’utilisateur. Ce processus détermine à quel moment et comment le contenu est préparé et envoyé au navigateur, impactant directement les performances, l’expérience utilisateur et la maintenabilité de votre code.

Depuis sa version 13, Next.js a introduction une nouvelle methode de routage, l’App Router qui permet d’optimiser ses méthodes de rendu en les adaptant aux besoins spécifiques de chaque composant. Dans ce guide, nous explorerons en détail les différentes stratégies de rendu, classées selon deux catégories fondamentales :

  • Server Components : Ces composants sont générés côté serveur avant d’être envoyés au client. Ils offrent des approches variées telles que le rendu statique, le rendu dynamique ou encore le streaming.
  • Client Components : Exécutés dans le navigateur, ils permettent d’offrir des interactions en temps réel grâce au rendu côté client.

Nous aborderons également l’Incremental Static Regeneration (ISR), qui permet de mettre à jour vos pages statiques sans devoir reconstruire l’ensemble de l’application.


Méthodes de Rendu Côté Serveur

Les méthodes de rendu côté serveur (Server Side Rendering - SSR) reposent sur l’utilisation des Server Components pour générer le contenu avant qu’il ne soit envoyé à l’utilisateur. Ces techniques permettent d’optimiser les performances et le référencement en offrant des pages pré-générées ou personnalisées à la volée.

C'est ainsi que nous distingons les methodes de rendu coté serveur suivantes :

  • Le rendu Statique
  • Le rendu Dynamique
  • Le streaming

Rendu Statique

Le Static Rendering génère les pages une fois pour toutes au moment de la construction (build time). Idéal pour le contenu non dynamique. durant ce processus, il est même possible d'ajouter des données externes à notre site s

Pages sans données externes

Ici, nous n'avons tout simplement recours à aucune methode pour extraitre des données d'une API ou d'une base de données quelconque.

Cette approche est intéressante pour les pages dont le contenu ne change presque jamais, les pages fixes comme « Contact », « À Propos ».

1// app/page.js
2export default function HomePage() {
3  return (
4    <div>
5      <h1>Bienvenue sur La Guilde des Codeurs !</h1>
6      <p>Ce contenu est généré lors du build, ce qui le rend ultra-rapide à charger.</p>
7    </div>
8  );
9}
10

Explications détaillées :

  • Processus de Build :
    Lors de la compilation de votre application, Next.js crée une version statique de cette page. Cela signifie qu’une fois générée, la page ne change pas tant que vous ne relancez pas un nouveau build.
  • Performance :
    Comme le contenu est déjà prêt, chaque visiteur reçoit immédiatement une page complète, ce qui améliore drastiquement le temps de chargement.

Avec Récupération de Données

Dans cette variante, Next.js génère des pages statiques en récupérant des données au build time. Ces données peuvent être issues d'une API ou d'une base de données.

C'est une approche intéressante pour des pages dont le contenu nécessitent d’être à jour lors de la construction, comme un des articles de blog qui sont récupérés depuis un un service externe (CMS headless, API par exemple).

1// app/blog/[slug]/page.js
2export async function generateStaticParams() {
3  // Récupérer la liste des articles depuis une API
4  const posts = await fetch('https://api.example.com/posts')
5    .then(res => res.json());
6
7  // Retourner un tableau d'objets contenant les slugs des articles
8  return posts.map(post => ({ slug: post.slug }));
9}
10
11export default async function BlogPost({ params }) {
12  const post = await fetch(`https://api.example.com/posts/${params.slug}`).then(res => res.json());
13  
14  return (
15    <div>
16      <h1>{post.title}</h1>
17      <p>{post.content}</p>
18    </div>
19  );
20}

Explications détaillées :

  • generateStaticParams récupère la liste des posts lors de la construction, et chacun d'eux devient alors une page statique.

Rendu Dynamique

Le rendu dynamique génère la page à chaque requête, ce qui est parfait lorsque vous devez afficher des informations qui changent fréquemment, des informations qui ne peuvent être connues qu'au moment de la requête.

1// app/dashboard/page.js
2export default async function Dashboard() {
3  const user = await fetch('https://api.example.com/user', { 
4    cache: 'no-store' // Désactive le cache
5  }).then(res => res.json());
6
7  return (
8    <div>
9      <h1>Bonjour {user.name} !</h1>
10      <p>Solde : {user.balance}</p>
11    </div>
12  );
13}

Explication :

  • cache: 'no-store' force Next.js à ignorer le cache, générant la page à chaque visite.

Passer du rendu dynamique au rendu statique

Pendant le rendu, si une API dynamique ou une option fetch avec { cache: 'no-store' } est détectée, Next.js opte automatiquement pour le rendu dynamique de l’ensemble de la route. Voici un résumé sous forme de tableau qui m’aide à expliquer ce comportement :

switching strategy

Ce que cela signifie pour nous en tant que développeur :

  • Pour qu'une route soit entièrement statique, toutes les données doivent être mises en cache.
  • Si une API dynamique ou une option fetch avec { cache: 'no-store' } est utilisée, Next.js bascule automatiquement vers un rendu dynamique pour toute la route.
  • Vous n’avez pas besoin de choisir explicitement entre rendu statique et dynamique : Next.js détermine la meilleure stratégie selon les fonctionnalités et les API utilisées. Votre rôle consiste à choisir comment et quand mettre en cache ou régénérer des données.

ISR (Incremental Static Regeneration)

L’ISR permet de générer des pages statiques et de les mettre à jour périodiquement sans rebuild l’application entière.

1// app/products/[id]/page.js
2export async function generateStaticParams() {
3  const products = await fetch('https://api.example.com/products').then(res => res.json());
4  return products.map(product => ({ id: product.id }));
5}
6
7export const revalidate = 3600; // Re-génère la page toutes les heures
8
9export default function Product({ params }) {
10  const product = await fetch(`https://api.example.com/products/${params.id}`).then(res => res.json());
11  
12  return (
13    <div>
14      <h1>{product.name}</h1>
15      <p>Stock : {product.stock}</p>
16    </div>
17  );
18}

Explication :

  • revalidate: 3600 met à jour la page toutes les heures.

Quand l’utiliser ?
Pour les sites e-commerce, catalogues, ou tout contemi semi-dynamique.


Client Components : L’Interactivité Côté Navigateur

Les Client Components sont exécutés dans le navigateur et permettent d’offrir des interactions en temps réel. Ils sont parfaits pour les éléments qui nécessitent une mise à jour dynamique sans recharger la page.

Cette approche est parfaite pour les formulaires, animations, ou toute logique nécessitant du JavaScript côté client.

1"use client"; // Directive obligatoire
2import { useState } from 'react';
3
4export default function SearchBar() {
5  const [query, setQuery] = useState('');
6
7  return (
8    <div>
9      <input 
10        type="text" 
11        value={query} 
12        onChange={(e) => setQuery(e.target.value)} 
13      />
14      <p>Recherche : {query}</p>
15    </div>
16  );
17}

Explication : "use client" est une directive qui indique que ce composant s’exécute côté client

NB : On ne peut utiliser les hooks react que s'il s'agit d'un composant coté client. C'est ce qui justifie la présence du hook useState qui nous aide à gérer l'état de la barre de recherche ici.


Streaming (Diffusion en Continu)

Le streaming permet de commencer à envoyer des parties de la page au navigateur dès qu’elles sont prêtes, sans attendre que l’ensemble du contenu soit généré. Cela améliore la réactivité, surtout pour les pages complexes. Il est intéréssant pour améliorer l’expérience utilisateur sur les pages avec des données lentes à charger.

Cette approche s'utilise tant avec les composants côtés serveurs que les composants côtés clients.

1// app/profile/page.js
2import { Suspense } from 'react';
3
4async function UserStats() {
5  const stats = await fetch('https://api.example.com/stats').then(res => res.json());
6  return <p>Visites : {stats.views}</p>;
7}
8
9export default function Profile() {
10  return (
11    <div>
12      <h1>Profil</h1>
13      <Suspense fallback={<p>Chargement des statistiques...</p>}>
14        <UserStats />
15      </Suspense>
16    </div>
17  );
18}

Explication : Suspense permet d’afficher un fallback pendant le chargement.

Conclusion

Next.js offre une palette de méthodes de rendu pour s’adapter à tous les besoins. Que vous construisiez un blog, une application métier, ou un site e-commerce, choisissez la stratégie qui équilibre performance, interactivité et maintenabilité. Avec le App Router, vous avez les outils pour créer des applications rapides, évolutives et engageantes.

  • Static Rendering : Contenu fixe ou rarement mis à jour.
  • Dynamic Rendering : Données personnalisées ou temps réel.
  • Streaming : Expérience utilisateur fluide malgré des données lentes.
  • Client Components : Interactivité et états locaux.
  • ISR : Contenu statique avec mises à jour périodiques.