Retour au blog

Next.js 16.3 et les Instant Navigations: Comment Avoir la Fluidite d'une SPA sans Renoncer au Serveur

Salut HaWkers, il y a une critique sur Next.js que vous avez probablement deja entendue (ou formulee vous-meme): "la navigation dans une app avec Server Components parait lente". Vous cliquez sur le lien, rien ne se passe pendant un instant, et seulement ensuite la page suivante apparait. Sur un site de contenu, ca passe inapercu. Dans une application, ca derange.

L'equipe de Next.js l'a reconnu publiquement et la reponse est arrivee avec la 16.3, sous la forme d'un ensemble de fonctionnalites appele Instant Navigations. Savez-vous ce qui change dans votre next.config.ts pour l'activer, et pourquoi Next.js a arrete de declencher un prefetch par lien? Voyons ca en pratique.

Le Vrai Probleme: Deux Ecarts Entre le Clic et l'Ecran

Avant de parler de solution, il vaut la peine de comprendre pourquoi la navigation server-driven prend du temps. Il existe deux ecarts distincts:

  1. Le client doit parler au serveur. Si la latence est elevee, ca coute cher, peu importe la rapidite de votre code.
  2. Le serveur doit generer la reponse. Si la requete est lente, l'utilisateur attend.

Une app client-driven resout ca parce qu'elle a deja le code de l'ecran suivant dans le bundle. Elle affiche un shell immediatement et va chercher les donnees ensuite. Next.js 16.3 attaque les deux ecarts separement, et cette distinction est la cle pour comprendre tout le reste.

Activer Cache Components

Tout le nouveau comportement se trouve derriere un flag. Premiere etape:

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
};

export default nextConfig;

Ce flag fait partie d'un mouvement plus large que Vercel mene depuis environ un an: ramener Next.js a ses origines, en etant dynamique par defaut, sans cache implicite ni cache cache. La documentation previent deja que cacheComponents deviendra le comportement par defaut dans une future version majeure.

Stream, Cache ou Block: C'est Vous Qui Choisissez

Avec le flag active, chaque fois qu'une route fait un await sur une donnee cote serveur, Next.js vous presente une decision explicite. Il y a trois chemins:

Stream avec <Suspense> - l'utilisateur voit un etat de chargement immediatement, et le reste arrive en streaming.

import { Suspense } from 'react';

export default function ProductPage({ params }) {
  return (
    <>
      <ProductHeader id={params.id} />
      <Suspense fallback={<InventorySkeleton />}>
        <InventoryStatus id={params.id} />
      </Suspense>
    </>
  );
}

Cache avec 'use cache' - l'utilisateur voit une UI deja mise en cache, reutilisee entre les requetes.

async function getFeaturedProducts() {
  'use cache';

  const products = await db.product.findMany({ where: { featured: true } });
  return products;
}

Dans les deux cas la navigation devient instantanee. Mais parfois vous voulez que la navigation attende le serveur. Un blog, par exemple, peut preferer ne jamais afficher de shell de chargement sur l'article. Pour ca, il existe un troisieme chemin:

// page.tsx ou layout.tsx
export const instant = false;

C'est l'oppose de la magie. Le framework ne decide pas a votre place: il vous oblige a declarer l'intention de chaque route.

Instant Insights: Une Navigation Lente Devient une Erreur

Voici la partie qui change le quotidien. En developpement, Next.js 16.3 traite une navigation non instantanee comme une erreur, affichee dans un panneau appele Instant Insights. Il indique exactement quelles routes ne naviguent pas instantanement, et pourquoi.

En pratique, ca inverse le flux de travail. Avant, la performance de navigation etait quelque chose que vous mesuriez apres coup, si vous y pensiez. Maintenant elle apparait pendant que vous ecrivez le code, exactement comme une erreur de typage.

Pour ne pas regresser apres un refactoring, un helper de test pour Playwright est aussi arrive:

import { expect, test } from '@playwright/test';
import { instant } from '@next/playwright';

test('le titre du produit apparait immediatement', async ({ page }) => {
  await page.goto('/products/shoes');

  // Verifie ce qui est visible sans attendre le reseau
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]');
    await expect(page.locator('h1')).toContainText('Casquette');
    await expect(page.getByText('Verification du stock...')).toBeVisible();
  });

  await expect(page.getByText('12 en stock')).toBeVisible();
});

Regardez ce que le test affirme: le titre doit etre visible avant que le reseau ne reponde, et l'etat de chargement du stock aussi. C'est une assertion sur la perception, pas sur le temps total.

Partial Prefetching: Un Shell par Route, Pas un par Lien

C'est le changement le plus interessant du point de vue de l'architecture.

Jusqu'a la 16.2, Next.js declenchait une requete de prefetch pour chaque lien dans le viewport. Si vous aviez une sidebar avec vingt conversations, ca faisait vingt requetes, toutes pointant vers la meme route /chat/[id]. Ceux qui ouvraient l'onglet Network en production voyaient ce deluge d'appels et se demandaient ce qui se passait.

La 16.3 emprunte l'astuce des SPA. Au lieu de chercher une page par lien, Next.js va chercher un shell reutilisable par route, et met ce shell en cache cote client. Vingt liens de chat deviennent un seul prefetch du shell de /chat/[id].

Conceptuellement c'est la meme chose que le code splitting par route dans une SPA: vous telechargez la structure une fois et vous la reutilisez.

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

export default nextConfig;

Comme les shells sont reutilises entre les liens, ils deviennent aussi une base pour la navigation hors ligne. L'equipe a deja signale qu'elle compte explorer des routes prefetchees qui restent navigables quand le reseau tombe pendant quelques secondes.

Quand Vous Avez Besoin de Plus Que le Shell

Reduire le prefetch a un cout: parfois vous voulez qu'un morceau de contenu bien precis apparaisse instantanement, pas seulement le squelette. Pour ca, <Link prefetch> existe toujours:

<Link href={`/chat/${id}`} prefetch={true}>
  {chat.title}
</Link>

Mais avec une difference importante: meme comme ca, Next.js ne va pas rendre la route entiere jusqu'au bout. Il rend jusqu'a l'endroit ou le contenu est disponible de facon synchrone, connu par l'URL (params, searchParams) ou marque avec 'use cache'.

Autrement dit, le choix "tout ou rien" du prefetch est termine. Le shell instantane est la ligne de base, et <Link prefetch> combine avec 'use cache' ajoute des couches par lien quand ca en vaut la peine.

Pour inspecter ce qui est exactement prefetche, les DevTools ont gagne un Navigation Inspector, qui met en pause chaque navigation au niveau du shell et vous laisse voir ce qui apparaitrait instantanement. Il faut se rappeler que le vrai prefetch n'a lieu qu'en production.

Une Feuille de Route de Migration Qui Ne Casse Pas Tout

Activer cacheComponents dans une grosse application et faire remonter des dizaines d'erreurs d'un coup n'aide personne. Le chemin qui a du sens est incremental:

1. Activez juste le flag et lisez le rapport. Ne changez pas encore de code. Instant Insights va lister les routes qui ne naviguent pas instantanement. Cet inventaire est votre backlog priorise, et a lui seul il dit deja ou votre application paye de la latence.

2. Classez chaque route avant d'y toucher. Pour chaque route de la liste, decidez lequel des trois chemins elle devrait suivre. Une route de lecture avec des donnees qui bougent peu appelle 'use cache'. Une route avec des donnees par utilisateur appelle <Suspense> autour de la partie lente. Une route qui a vraiment besoin de la donnee avant de peindre (checkout, confirmation de paiement) merite export const instant = false, et ce n'est pas une defaite.

3. Commencez par le layout, pas par la page. Header, sidebar et navigation sont souvent les memes d'une route a l'autre et sont les meilleurs candidats au shell reutilisable. Resoudre le layout ameliore plusieurs routes d'un coup.

4. Seulement apres, activez partialPrefetching. Avec les routes deja classees, le prefetch par shell a de quoi reutiliser. L'activer avant ne fait qu'echanger un deluge de requetes contre des shells vides.

5. Verrouillez le resultat avec des tests. Chaque route que vous avez rendue instantanee gagne un test avec le helper instant(). Sans ca, le premier refactoring defait le travail et personne ne s'en apercoit avant que la plainte n'arrive.

Il faut dire qu'il existe une Skill officielle publiee dans le depot de Next.js pour conduire l'adoption de Cache Components avec un agent d'IA, si vous preferez deleguer le balayage initial.

Quoi d'Autre dans la 16.3

Instant Navigations est le titre principal, mais cette version apporte des gains qui touchent meme les applications qui n'adopteront pas les nouveaux flags:

  • Memoire en developpement: les longues sessions de next dev utilisent jusqu'a 90% de RAM en moins
  • Builds repetes: les artefacts qui n'ont pas change sont lus depuis le cache
  • Type checking: next build peut utiliser TypeScript 7 pour la verification des types
  • Rendu cote serveur: jusqu'a 22% de requetes traitees en plus sous charge, avec un usage natif des streams de Node.js
  • Documentation versionnee pour les agents: les outils d'IA lisent la doc de la bonne version sans configuration
  • Imports avec glob: importer plusieurs fichiers via une nouvelle API de Turbopack

Le gain de memoire en dev, a lui seul, justifie deja la mise a jour dans un gros monorepo.

Ca Vaut le Coup d'Adopter Maintenant?

Deux choses pesent dans la decision.

La premiere, c'est que Vercel elle-meme a utilise tout ca en production avant de le publier. v0 a beaucoup d'interaction cote client et ses navigations laissaient a desirer depuis un moment. Instant Insights a pointe les routes problematiques et l'equipe les a corrigees une par une. L'equipe promet de detailler les patterns adoptes dans un prochain article.

La seconde, c'est qu'il reste des problemes connus en Preview. Les outils d'Instant Insights ont des defauts sur Safari, donc en developpement mieux vaut utiliser Chrome ou Firefox. Et, avec Partial Prefetching active, acceder aux params a l'interieur d'un shell fait bloquer la route sans que ce soit remonte comme Instant Insight, meme si le Navigation Inspector et le helper instant() restent corrects.

Pour tester:

npm install next@preview

Ma lecture: si vous maintenez une application avec beaucoup de navigation interne (dashboard, chat, panneau d'administration), ca vaut le coup d'ouvrir une branche et d'activer cacheComponents juste pour voir ce qu'Instant Insights signale. Le rapport a lui seul est deja un diagnostic precieux, meme si vous n'adoptez rien pour l'instant. Si votre produit est un site de contenu, l'urgence est bien moindre.

Ce Que Ca Dit sur la Direction de Next.js

Il y a ici un pattern qui merite d'etre nomme. Next.js a passe des annees a ajouter du cache implicite, puis a passe les dernieres annees a le retirer. Les Instant Navigations suivent la meme philosophie que la phase actuelle: rien ne se passe dans votre dos, et le framework vous force a declarer l'intention de chaque route, que ce soit Stream, Cache ou Block.

C'est un echange assume. Vous ecrivez une ligne de configuration de plus par route, et en echange vous savez exactement ce qui se passe quand quelqu'un clique sur un lien. Apres des annees a debugger du cache que personne n'avait demande, ca ressemble a une bonne affaire.

Si vous voulez comprendre la base sur laquelle tout ca a ete construit, je vous recommande de consulter un autre article: React Server Components en Production: Le Guide Complet ou je couvre les patterns d'architecture que les Instant Navigations supposent que vous connaissez deja.

Allez, on y va! 🦅

📚 Vous Voulez Suivre Ce Qui Arrive?

Cet article a couvert Next.js 16.3 et les Navigations Instantanees, mais l'ecosysteme change chaque semaine et tout ne devient pas un article ici.

Sur X je partage ce que je teste, les coulisses de mes projets et les nouveautes qui apparaissent avant de devenir un post.

Suivez-Moi La-Bas

👉 Suivre @jeffbruchado sur X

💡 Du contenu quotidien sur le developpement, la carriere et les outils que j'utilise vraiment

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires