Aller au contenu

Routage dans Next.js

Routage basé sur les fichiers

Dans Next.js, le routage est basé sur la structure des dossiers dans le répertoire app/. Chaque dossier correspond à un segment de l'URL, et un fichier page.tsx dans ce dossier rend la route accessible.

app/
├── page.tsx              → /
├── a-propos/
│   └── page.tsx          → /a-propos
└── produits/
    ├── page.tsx           → /produits
    └── [id]/
        └── page.tsx       → /produits/1, /produits/2, etc.

Les fichiers spéciaux

Next.js reconnaît plusieurs fichiers spéciaux dans chaque dossier de route :

Fichier Rôle
page.tsx Le contenu de la page (rend la route accessible)
layout.tsx Gabarit partagé qui enveloppe les pages enfants
loading.tsx Interface de chargement affichée pendant le chargement
error.tsx Interface d'erreur affichée en cas de problème

page.tsx

Fichier obligatoire pour qu'une route soit accessible. Il exporte le composant qui sera affiché :

app/page.tsx
export default function Accueil() {
  return (
    <main>
      <h1>Page d&apos;accueil</h1>
      <p>Bienvenue sur notre application Next.js!</p>
    </main>
  );
}
app/a-propos/page.tsx
export default function APropos() {
  return (
    <main>
      <h1>À propos</h1>
      <p>Cette application est un exemple de routage avec Next.js.</p>
    </main>
  );
}

layout.tsx

Le layout enveloppe les pages enfants. Il est idéal pour les éléments de navigation partagés :

app/layout.tsx
import Link from "next/link";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="fr">
      <body>
        <nav>
          <Link href="/">Accueil</Link> |{" "}
          <Link href="/produits">Produits</Link> |{" "}
          <Link href="/a-propos">À propos</Link>
        </nav>
        <hr />
        {children}
      </body>
    </html>
  );
}

Le composant {children} sera remplacé par le contenu de la page active. Ceci est similaire au concept de &lt;Outlet /&gt; dans React Router.

loading.tsx

Affiche un indicateur de chargement pendant que la page se charge :

app/produits/loading.tsx
export default function Loading() {
  return <p>Chargement des produits...</p>;
}

error.tsx

Gère les erreurs dans un segment de route. Ce fichier doit obligatoirement être un Client Component ("use client") :

app/produits/error.tsx
"use client";

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>Une erreur est survenue!</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Réessayer</button>
    </div>
  );
}

Routes dynamiques avec [parametre]

Pour créer une route dynamique, on utilise un nom de dossier entre crochets. Par exemple, app/produits/[id]/page.tsx accepte n'importe quelle valeur pour id.

app/produits/[id]/page.tsx
interface ProduitPageProps {
  params: Promise<{ id: string }>;
}

export default async function ProduitDetail({ params }: ProduitPageProps) {
  const { id } = await params;

  return (
    <main>
      <h1>Détail du produit #{id}</h1>
      <p>Vous consultez le produit avec l&apos;identifiant : {id}</p>
    </main>
  );
}

Le paramètre est accessible via la propriété params du composant. Dans Next.js 15+, params est une Promise qu'il faut attendre avec await.

Pour naviguer entre les pages, utilisez le composant &lt;Link&gt; de Next.js plutôt que des balises &lt;a&gt; classiques. Le composant &lt;Link&gt; effectue une navigation côté client sans recharger la page complète.

app/produits/page.tsx
import Link from "next/link";

const produits = [
  { id: 1, nom: "Clavier mécanique" },
  { id: 2, nom: "Souris ergonomique" },
  { id: 3, nom: "Écran 27 pouces" },
];

export default function Produits() {
  return (
    <main>
      <h1>Liste des produits</h1>
      <ul>
        {produits.map((produit) => (
          <li key={produit.id}>
            <Link href={`/produits/${produit.id}`}>{produit.nom}</Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

Routes imbriquées et layouts partagés

Les layouts sont partagés entre les routes enfants. Lorsque vous naviguez entre des pages qui partagent un layout, seul le contenu de la page change, pas le layout.

app/
├── layout.tsx            ← Layout racine (navigation principale)
├── page.tsx              ← Page d'accueil
└── produits/
    ├── layout.tsx        ← Layout pour la section produits
    ├── page.tsx          ← Liste des produits
    └── [id]/
        └── page.tsx      ← Détail d'un produit

Avec cette structure, le layout racine (app/layout.tsx) enveloppe tout, et le layout des produits (app/produits/layout.tsx) enveloppe seulement les pages de la section produits.

Comparaison avec React Router

Concept React Router Next.js
Définition des routes &lt;Route path="/produits" element={&lt;Produits /&gt;} /&gt; Dossier app/produits/page.tsx
Routes dynamiques &lt;Route path="/produits/:id" ... /&gt; Dossier app/produits/[id]/page.tsx
Accès aux paramètres useParams() Propriété params du composant
Navigation &lt;Link to="/produits"&gt; &lt;Link href="/produits"&gt;
Layout partagé &lt;Outlet /&gt; dans un composant parent layout.tsx avec {children}
Chargement Géré manuellement loading.tsx automatique
Erreurs Géré manuellement error.tsx automatique