Routage et navigation avancée
Comprendre l’évolution du routage dans Next.js
1. Pourquoi deux systèmes de routage ?
Next.js, depuis ses débuts, s’est distingué par son système de routage basé sur la structure des fichiers. Jusqu’à la version 12, ce mécanisme reposait exclusivement sur le Pages Router, une solution simple et efficace : chaque fichier dans le dossier pages/ correspondait directement à une route de l’application. Cette convention a grandement contribué à la popularité de Next.js en réduisant la configuration nécessaire pour créer une SPA (Single Page Application) optimisée et performante.
Cependant, avec l’évolution de React - notamment l’arrivée des server components, du streaming via Suspense, et des layouts imbriqués - les limites du Pages Router sont devenues plus apparentes. Next.js 13 a donc introduit un nouveau paradigme : l’App Router. Plutôt que de venir remplacer brutalement l’ancien système, ce nouveau routeur a d’abord été proposé en parallèle, laissant aux développeurs la possibilité d’adopter progressivement cette nouvelle architecture.
Le Pages Router n’est pas supprimé : il continue de fonctionner pour des raisons de rétrocompatibilité. Mais il n’évoluera plus et ne prend pas en charge les fonctionnalités modernes de React.
Ce changement structurel n’est pas anodin : il implique une réorganisation du code, une compréhension plus poussée des comportements côté serveur et client, et l’adoption de nouvelles conventions. Mais il reflète aussi une ambition claire : permettre à Next.js de devenir la plateforme de référence pour les applications React modernes, capable de tirer pleinement parti des innovations introduites dans le cœur même de React.
En résumé, l’App Router est une réponse directe aux évolutions du Web moderne. Il ne s’agit pas simplement d’un changement d’API, mais d’un changement de paradigme, qui rapproche encore davantage Next.js d’un framework full-stack, performant et évolutif. Le Pages Router, quant à lui, reste utilisable, mais s’adresse surtout...
Routage dynamique avec l’App Router
1. Introduction : routes dynamiques et segments
Dans une application web, il est courant d’avoir besoin d’afficher des pages différentes selon un paramètre présent dans l’URL. Par exemple, pour un blog, chaque article doit être accessible via une URL unique comme /blog/mon-article. Pour une plateforme utilisateur, chaque profil peut correspondre à une route de type /users/john.
C’est à cela que servent les routes dynamiques : elles permettent de capturer une partie variable de l’URL et d’en faire un paramètre accessible dans le code. Le composant affiché peut alors s’adapter dynamiquement à cette valeur.
L’App Router conserve ce principe, hérité du Pages Router, tout en l’intégrant dans une structure modulaire basée sur les dossiers. Chaque segment dynamique est représenté par un dossier nommé entre crochets, comme [slug] ou [id], à l’intérieur du répertoire app/.
Cette nouvelle organisation s’inscrit dans la logique globale de l’App Router : découper l’interface en segments d’URL explicites, chacun possédant sa propre page, son propre layout et ses éventuels états de chargement ou de gestion d’erreur. Le routage dynamique reste donc simple à mettre en œuvre, tout en s’adaptant à une architecture plus flexible et évolutive.
2. Créer une route dynamique : [param]
Pour créer une route dynamique avec l’App Router, il suffit de créer un dossier nommé entre crochets à l’intérieur du dossier app/. Ce nom représente le paramètre dynamique que l’on souhaite capturer dans l’URL.
app/
├─ blog/
│ ├─ [slug]/
│ │ └─ page.tsx
Dans cet exemple :
-
Le dossier [slug] correspond à une route dynamique.
-
slug est le nom du paramètre que l’on récupérera dans le code.
-
Le fichier page.tsx définit la page affichée pour chaque valeur de slug.
Ainsi, les URL comme /blog/article-1 ou /blog/hello-world pointeront toutes vers app/blog/[slug]/page.tsx, avec une valeur différente pour le paramètre...
Navigation côté client
1. Rappel : structure du routage dans l’App Router
Depuis Next.js 13, l’App Router propose une approche modulaire et puissante pour gérer le routage, entièrement conçue en TypeScript. Dans cette architecture, le répertoire app/ joue un rôle central, et chaque dossier correspond à un segment d’URL. Les fichiers clés, tels que page.tsx, layout.tsx, loading.tsx ou error.tsx, définissent respectivement la page, le layout, l’état de chargement et la gestion des erreurs pour le segment concerné.
Structure de base de l’App Router en TypeScript :
-
Répertoire app/ : point d’entrée du routage. Chaque sous-dossier représente un segment d’URL.
-
page.tsx : fichier principal qui exporte un composant React (écrit en TypeScript) pour rendre la page du segment.
-
layout.tsx : définit un layout partagé pour les pages du segment et peut être imbriqué avec d’autres layouts.
-
loading.tsx : gère l’affichage d’un état de chargement (par exemple, un spinner ou un skeleton) pendant le chargement des composants ou des données.
-
error.tsx : permet de définir le rendu en cas d’erreur dans le segment, offrant une meilleure expérience utilisateur.
Par exemple, une arborescence de fichiers TypeScript pourrait ressembler à ceci :
app/
├─ about/
│ ├─ page.tsx
│ └─ layout.tsx
├─ blog/
│ ├─ [slug]/
│ │ └─ page.tsx
│ ├─ page.tsx
│ └─ layout.tsx
└─ layout.tsx
Cette structure se traduit par :
-
/about : géré par app/about/page.tsx, avec un layout spécifique défini dans app/about/layout.tsx qui peut hériter du layout global de app/layout.tsx.
-
/blog : géré par app/blog/page.tsx.
-
/blog/[slug] : route dynamique pour afficher des articles de blog, où le paramètre [slug] est typé et accessible...
Cas pratique - Combiner les méthodes de navigation
Après avoir exploré séparément <Link>, useRouter et redirect, il est intéressant de voir comment ces trois outils peuvent s’articuler dans un scénario plus complet. Nous allons proposer une petite étude de cas pour illustrer leur complémentarité, en mettant l’accent sur :
-
la navigation de base via <Link> ;
-
l’adaptation dynamique de l’URL et la pagination grâce à useRouter.
La sécurisation de l’accès à certaines pages via redirect côté serveur
1. Étude de cas : une application de blog sécurisée
Supposons que l’on souhaite développer une application de blog avec plusieurs fonctionnalités :
-
un affichage public de la liste des articles, paginée et filtrable ;
-
une zone d’administration réservée aux rédacteurs, nécessitant une authentification ;
-
une redirection automatique des utilisateurs non authentifiés vers la page de connexion.
Structure simplifiée :
app/
├─ admin/
│ ├─ page.tsx
│ └─ layout.tsx
├─ blog/
│ ├─ page.tsx
│ └─ [slug]/
│ └─ page.tsx
├─ login/
│ └─ page.tsx
└─ layout.tsx
-
/blog : affiche la liste des articles, avec une pagination et un filtre ;
-
/blog/[slug] : affiche le détail d’un article ;
-
/admin : zone réservée aux utilisateurs authentifiés ;
-
/login : page de connexion.
a. Navigation de base avec <Link>
Liste d’articles (extrait de app/blog/page.tsx) :
import Link from 'next/link';
export default async function BlogPage() {
const articles = await fetchArticles(); // Récupération côté serveur
return (
<div>
<h1>Liste des articles</h1> ...Structurer l’interface avec les layouts
1. Pourquoi structurer l’interface avec des layouts ?
Dans une application web, certaines zones de l’interface doivent rester constantes d’une page à l’autre : l’en-tête, la navigation, le pied de page, ou encore une barre latérale. Ces éléments structurants assurent la cohérence visuelle et facilitent la navigation pour l’utilisateur.
Avec le Pages Router, cette cohérence reposait sur des solutions artisanales, comme le composant _app.tsx ou des wrappers personnalisés. Chaque développeur devait inventer sa propre méthode pour maintenir des éléments partagés entre les pages, au risque d’une logique difficile à maintenir.
L’App Router introduit une nouvelle façon de penser l’interface. Grâce aux fichiers layout.tsx, il devient possible de définir des layouts modulaires et persistants à chaque niveau de l’arborescence. Cette architecture permet de structurer l’application de manière claire, évolutive et optimisée. Elle favorise également une meilleure séparation entre la structure (layouts) et le contenu (pages).
2. Le rôle de layout.tsx
a. Fichier clé du segment
Le fichier layout.tsx joue un rôle central dans l’organisation de l’interface avec l’App Router. Il peut être placé à n’importe quel niveau de l’arborescence située dans le dossier app/.
Lorsqu’il est présent dans un segment, ce fichier définit le cadre visuel et structurel qui s’applique à toutes les routes enfants de ce segment. Cela permet d’encapsuler un ensemble de pages dans un même environnement (par exemple, une section administrateur avec sa propre navigation latérale), sans impacter le reste de l’application.
Cette logique favorise une composition modulaire, où chaque partie de l’application peut définir ses propres règles d’affichage tout en héritant éventuellement de layouts parents.
b. Exemple de structure
Voici un exemple d’arborescence typique illustrant l’utilisation imbriquée des fichiers layout.tsx :
app/
├─ layout.tsx ->...Middleware et interception de requêtes
1. Pourquoi intercepter une requête ?
Dans une application Next.js, le traitement d’une requête suit un cycle précis : réception côté serveur (ou Edge), vérification des droits d’accès ou du contexte, exécution de la logique, puis rendu de la page. Jusqu’à Next.js 12, la plupart des logiques conditionnelles (comme la redirection ou le filtrage) étaient réalisées pendant ou après le rendu, à travers des fonctions comme getServerSideProps, ou directement dans les composants React.
L’arrivée des middlewares dans Next.js 13 change la donne. Il est désormais possible d’intercepter une requête dès sa réception, avant même que le code de la page ne soit évalué. Ces middlewares s’exécutent dans un environnement Edge distribué, ce qui leur confère à la fois légèreté, rapidité et puissance.
Cette interception permet notamment :
-
de rediriger un utilisateur non authentifié vers une page de connexion sans charger la moindre ressource privée ;
-
de forcer une langue ou une région en fonction de la géolocalisation ;
-
de réécrire une URL de manière transparente pour le navigateur, sans provoquer de redirection visible ;
-
d’ajouter des en-têtes personnalisés pour des besoins de sécurité, de suivi ou de diagnostic ;
-
de bloquer certaines IP ou de détecter automatiquement du trafic robotique ou malveillant.
En résumé, le middleware devient un filtre de bas niveau, capable d’agir très tôt dans le cycle de traitement. Il permet de poser des règles globales qui s’appliquent à toutes (ou certaines) requêtes HTTP, sans impacter la logique métier spécifique aux pages.
Contrairement aux fonctions serveur classiques (getServerSideProps, layout.tsx, etc.), un middleware ne retourne pas de données, et ne déclenche pas de rendu. Son rôle est de modifier, rediriger, réécrire ou bloquer la requête, mais jamais de générer une réponse HTML.
Cette distinction est essentielle. Elle permet de concevoir une couche intermédiaire, indépendante...