Next.js 15 marque une rupture nette avec les conventions établies par les versions précédentes. L’App Router, sorti en stable avec Next.js 13 et consolidé à chaque release depuis, repose sur les React Server Components — un paradigme qui change fondamentalement la manière de penser l’architecture frontend. En 2026, avec Next.js 15.2 et les Partial Prerendering en GA, ignorer ces évolutions revient à se priver d’avantages concurrentiels significatifs en performance et en DX. Ce guide décortique les concepts essentiels et leurs applications concrètes.
App Router vs Pages Router : pourquoi migrer maintenant ?
Le Pages Router de Next.js (dossier /pages) reste fonctionnel et sera maintenu encore plusieurs années. Mais il n’accueille plus de nouvelles fonctionnalités depuis Next.js 13. Toute l’innovation — Server Components, Server Actions, Partial Prerendering, Parallel Routes, Intercepting Routes — vit exclusivement dans l’App Router (dossier /app). Si vous démarrez un nouveau projet, il n’y a plus aucune raison de choisir le Pages Router.
La migration depuis Pages vers App Router est progressive par design. Vous pouvez avoir les deux dossiers coexistant dans un même projet pendant toute la durée de la migration. Commencez par les pages les plus simples (pages statiques, pages sans état utilisateur), migrez le layout global en dernier. Vercel fournit un guide de coexistence détaillé qui liste les incompatibilités connues : les hooks useRouter (import différent), getServerSideProps (remplacé par fetch dans les Server Components), et les middlewares ont des comportements légèrement différents.
Le bénéfice principal de la migration n’est pas la performance brute — le Pages Router avec SSR était déjà très performant. C’est la réduction du JavaScript envoyé au client. Avec les Server Components, les composants qui n’ont pas besoin d’interactivité (la majorité du contenu affiché) ne génèrent aucun JS bundle côté client. Sur une application complexe, cela peut réduire le bundle client de 40 à 60 %, avec un impact direct sur le LCP et le TTI.
React Server Components : penser différemment le rendu
Les React Server Components (RSC) s’exécutent exclusivement sur le serveur — jamais dans le navigateur. Ils peuvent accéder directement aux bases de données, aux fichiers système, aux variables d’environnement secrètes, et appeler des APIs sans exposer les credentials. Leur sortie est du HTML (ou une représentation sérialisée spéciale) envoyé au client. Ils ne peuvent pas utiliser useState, useEffect, les event handlers, ou le contexte React.
Dans l’App Router, tous les composants sont des Server Components par défaut. Pour créer un Client Component (interactif, avec state), ajoutez la directive ‘use client’ en première ligne du fichier. Cette directive est virale vers le bas de l’arbre mais pas vers le haut : un Client Component peut importer et utiliser des Client Components enfants, mais il peut aussi recevoir des Server Components comme children (passés via props) — une nuance importante pour l’optimisation.
Le pattern de composition recommandé est de garder les composants feuilles interactifs en Client Component et de remonter la logique de données vers des composants parents Server. Un exemple concret : un composant ProductList (Server) qui fetche les données et passe les items à un composant ProductCard (Client) qui gère le hover, le favoris, et l’ajout au panier. Le rendu initial est serveur (rapide, SEO-friendly), les interactions sont client (fluides).
Server Actions : mutations sans API routes
Les Server Actions sont des fonctions async marquées avec la directive ‘use server’ qui s’exécutent sur le serveur mais peuvent être appelées directement depuis des composants Client. Elles remplacent élégamment les API routes pour les mutations de données : soumission de formulaires, mise à jour d’enregistrements, authentification. Sous le capot, Next.js génère automatiquement un endpoint POST sécurisé avec protection CSRF.
La puissance des Server Actions vient de leur intégration native avec les formulaires HTML. En attachant une Server Action à l’attribut action d’un formulaire, vous obtenez une soumission qui fonctionne même sans JavaScript (progressive enhancement), avec revalidation automatique du cache Next.js via revalidatePath() ou revalidateTag(). Pour l’UX, useFormStatus() permet d’afficher un état de chargement pendant l’exécution.
Attention aux Server Actions et à la sécurité : elles sont publiquement accessibles par construction (elles génèrent un endpoint HTTP). Ne faites jamais confiance aux données entrantes sans validation — utilisez zod ou valibot pour valider les inputs. La directive ‘use server’ ne rend pas une fonction privée, elle détermine seulement où elle s’exécute. Les autorisations doivent être vérifiées explicitement dans le corps de la fonction.
Exemples de Server Action et layout imbriqué
Voici un exemple concret de Server Action pour la mise à jour d’un profil utilisateur, avec validation et revalidation du cache :
// app/profile/actions.ts
"use server"
import { z } from "zod"
import { revalidatePath } from "next/cache"
import { auth } from "@/lib/auth"
import { db } from "@/lib/db"
const UpdateProfileSchema = z.object({
name: z.string().min(2).max(50),
bio: z.string().max(500).optional(),
website: z.string().url().optional().or(z.literal("")),
})
export async function updateProfile(formData: FormData) {
const session = await auth()
if (!session?.user) {
return { error: "Non autorisé" }
}
const raw = {
name: formData.get("name"),
bio: formData.get("bio"),
website: formData.get("website"),
}
const validated = UpdateProfileSchema.safeParse(raw)
if (!validated.success) {
return { error: validated.error.flatten().fieldErrors }
}
await db.user.update({
where: { id: session.user.id },
data: validated.data,
})
revalidatePath("/profile")
return { success: true }
}
// app/profile/page.tsx (Server Component)
export default async function ProfilePage() {
const session = await auth()
const user = await db.user.findUnique({ where: { id: session!.user.id } })
return (
<form action={updateProfile}>
<input name="name" defaultValue={user?.name ?? ""} />
<textarea name="bio" defaultValue={user?.bio ?? ""} />
<SubmitButton />
</form>
)
}
Caching, revalidation et Partial Prerendering
Le système de cache de Next.js 15 a été significativement révisé par rapport à Next.js 13-14. Le cache fetch() côté serveur est maintenant opt-in (cache: ‘force-cache’) plutôt qu’opt-out. Par défaut, les requêtes fetch dans les Server Components ne sont plus mises en cache — un changement important qui casse certains comportements attendus lors de la migration depuis des versions antérieures.
Le Partial Prerendering (PPR), passé en GA dans Next.js 15, permet de mélanger contenu statique et dynamique dans la même page sans compromettre les performances. La partie statique (navigation, shell de la page) est générée au build et servie depuis le CDN en quelques millisecondes. Les parties dynamiques (données utilisateur, inventaire temps réel) sont streamées depuis le serveur avec Suspense boundaries. Le résultat : des TTFB < 100ms même pour des pages personnalisées.
Pour la revalidation du cache, Next.js propose deux mécanismes : la revalidation basée sur le temps (revalidate: 3600 dans les options fetch ou le segment config) et la revalidation à la demande (revalidatePath(), revalidateTag() dans les Server Actions ou les Route Handlers). Pour les données qui changent peu souvent (catalogue produits, articles de blog), préférez la revalidation temporelle. Pour les données sensibles aux actions utilisateur (panier, profil), utilisez la revalidation à la demande.
Routing avancé : Parallel Routes et Intercepting Routes
Les Parallel Routes permettent d’afficher plusieurs pages simultanément dans le même layout, chacune avec son propre loading et error boundary. Le cas d’usage classique est un dashboard avec plusieurs panneaux indépendants — chaque panneau se charge et gère ses erreurs indépendamment sans bloquer les autres. Syntaxiquement, on crée des dossiers @nomSlot dans le layout, qui deviennent des props du Layout component.
Les Intercepting Routes permettent d’afficher une route dans un contexte différent selon la navigation. L’exemple emblématique : un grid de photos où cliquer sur une photo ouvre une modal (navigation côté client) mais accéder directement à l’URL de la photo affiche la page complète (navigation directe ou SSR). Cela évite de dupliquer la logique d’affichage entre modal et page dédiée — un pattern puissant pour les galeries, les produits, les posts.
Ces deux fonctionnalités avancées peuvent sembler complexes au premier abord, mais elles résolvent des problèmes réels d’UX que les développeurs contournaient auparavant avec des state globaux compliqués ou des portals React. L’apprentissage est justifié pour les applications avec des besoins d’UX sophistiqués — moins pour un blog ou un site vitrine standard.
Déploiement et environnements : au-delà de Vercel
Vercel est la plateforme de déploiement naturelle pour Next.js, et pour cause : Vercel développe Next.js. Mais en 2026, plusieurs alternatives matures permettent de déployer des applications Next.js App Router sans vendor lock-in. Netlify supporte les Server Components et les Server Actions en GA. AWS via SST (Serverless Stack) ou OpenNext propose un déploiement sur Lambda@Edge et CloudFront. Coolify ou Kamal permettent un self-hosting sur VPS.
Le self-hosting Next.js 15 en mode standalone (output: ‘standalone’ dans next.config.js) génère un bundle Node.js autonome d’environ 50-100 Mo selon l’application. Ce bundle tourne avec node server.js et peut être dockerisé facilement. Les fonctionnalités avancées comme le PPR et l’ISR fonctionnent en self-hosted, mais le cache est en mémoire ou nécessite une configuration Redis explicite pour la persistance entre instances.
Pour les applications à fort trafic en self-hosted, le point critique est la gestion du cache entre plusieurs instances. Next.js propose un CacheHandler personnalisable qui peut pointer vers Redis ou un autre store distribué. Sans cela, chaque instance a son propre cache isolé, ce qui peut créer des incohérences dans les données affichées selon l’instance qui traite la requête. La configuration est documentée mais demande une vigilance particulière en setup multi-instances.
Commentaires (0)
Laisser un commentaire
Les commentaires sont modérés. Questions WordPress, cybersécurité ou dev web bienvenues.