Next.js App Router : ce que ça change concrètement
Le rendu serveur devient le comportement par défaut
Avec l'App Router, le rendu serveur devient le comportement par défaut, et c'est le changement de fond. Dans le Pages Router, un composant partait par défaut dans le navigateur, et il fallait choisir getServerSideProps ou getStaticProps pour faire travailler le serveur. Dans l'App Router, chaque composant est un React Server Component tant que je ne déclare pas l'inverse. Le code s'exécute sur le serveur, ne rejoint jamais le bundle client, et le navigateur reçoit du HTML déjà rempli.
L'effet concret est une réduction du JavaScript envoyé. Un composant qui affiche des données ne traîne plus avec lui sa logique de fetch ni ses dépendances côté client. Pour un site vitrine ou un blog, des pages entières partent sans une ligne de JS applicatif. Sur les Core Web Vitals, et donc sur le SEO, ça se sent directement.
Server Components et Client Components : la nouvelle frontière
La nouvelle frontière sépare ce qui calcule sur le serveur de ce qui réagit dans le navigateur. Un Server Component lit la base de données, appelle une API privée, manipule des secrets, et tout cela reste côté serveur. Un Client Component, marqué par la directive « use client » en tête de fichier, gère l'interactivité : état, effets, écouteurs d'événements, hooks comme useState ou useEffect.
Le réflexe que j'ai pris est de garder les Client Components petits et en bout de chaîne. Plutôt que de marquer une page entière « use client » parce qu'un bouton a besoin d'un état, j'isole ce bouton dans son propre composant client et je laisse le reste de la page côté serveur. La page reste légère, et seul le fragment vraiment interactif voyage jusqu'au navigateur.
Le piège classique, c'est d'importer un Server Component dans un Client Component : ça ne marche pas comme on l'imagine. La bonne pratique est de passer les Server Components en children ou en props au composant client. On garde ainsi le serveur en haut de l'arbre et le client en feuilles.
Le routage par dossiers et les fichiers spéciaux
Le routage par dossiers fait du système de fichiers la définition de l'interface, pas seulement des URL. Dans le dossier app, chaque route est un dossier qui contient un fichier page. Mais l'App Router ajoute des fichiers à convention forte : layout pour l'enveloppe partagée et persistante, loading pour l'état de chargement, error pour la gestion d'erreur, not-found pour le 404, template pour un remontage à chaque navigation.
Les layouts changent vraiment la donne. Un layout enveloppe ses routes enfants et ne se remonte pas quand on navigue à l'intérieur. La barre de navigation, le pied de page, un état global d'interface restent en place pendant que seul le contenu change. Sur ce portfolio, j'utilise template pour jouer une transition de page à chaque changement de route, là où un layout, lui, resterait figé.
Le streaming et Suspense changent la perception de vitesse
Le streaming change la perception de vitesse en envoyant la page en morceaux au lieu d'attendre qu'elle soit complète. Avec un fichier loading ou une balise Suspense, le serveur expédie d'abord la coquille de la page, puis remplit les zones lentes au fur et à mesure que leurs données arrivent. L'utilisateur voit du contenu immédiatement, même si un bloc qui interroge une API lente n'est pas encore prêt.
Concrètement, j'enveloppe les parties coûteuses dans Suspense avec un squelette comme fallback. Le reste de la page s'affiche sans attendre. Cette mécanique évite l'ancien tout-ou-rien où une seule requête lente bloquait l'écran entier. Elle demande en revanche de réfléchir à ce qui doit s'afficher en premier, et de soigner les états de chargement pour qu'ils ne provoquent pas de décalage visuel.
Faut-il migrer un projet existant ?
Migrer un projet existant n'est pas obligatoire, et je ne le recommande pas par principe. Le Pages Router reste pris en charge, et les deux routeurs cohabitent dans une même application. Un projet stable qui tourne bien n'a aucune raison de tout réécrire pour suivre la mode. Une migration coûte du temps et introduit des risques, surtout sur les conventions de data fetching qui changent en profondeur.
Mon critère est simple : je pars sur l'App Router pour tout nouveau projet, parce que c'est la direction du framework et que les bénéfices de rendu sont réels dès le départ. Pour l'existant, je migre route par route quand une page a besoin d'évoluer, et seulement si le gain le justifie. La cohabitation permet cette progressivité, et c'est elle qui rend la transition saine plutôt que brutale.
Le cahier des charges en une page