Routing React.js : guide React Router v6

🏷️ Front-end 📅 15/04/2026 10:00:00 👤 Mezgani said
React React Router Routing Javascript Spa Lazy Loading
Routing React.js : guide React Router v6

Maîtrisez le routing dans React.js avec React Router v6 : routes imbriquées, navigation programmatique, lazy loading et protection de routes.

Pourquoi React Router v6 ?

React ne propose pas de système de routage natif. C'est React Router, la bibliothèque de référence, qui prend en charge la navigation côté client dans les SPA (Single Page Applications). La version 6, sortie fin 2021, apporte une API simplifiée, des routes imbriquées plus intuitives et un meilleur support de TypeScript.

Comparé à la v5, React Router v6 supprime <Switch> au profit de <Routes>, rend le matching exact par défaut, et introduit le hook useNavigate à la place de useHistory.

À retenir : React Router v6 est la version stable recommandée en 2024-2026. Si vous utilisez encore la v5, une migration progressive est possible.

Installation et configuration

Installez React Router avec npm ou yarn :

# npm
npm install react-router-dom

# yarn
yarn add react-router-dom

Ensuite, enveloppez votre application avec le composant BrowserRouter dans votre fichier d'entrée :

// main.jsx (ou index.jsx)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);
Note : BrowserRouter utilise l'API History du navigateur (URLs propres sans #). Pour des environnements sans serveur configuré, préférez HashRouter.

Routes de base : Routes et Route

<Routes> remplace <Switch> et sélectionne automatiquement la route la plus précise. Chaque <Route> associe un chemin à un composant via la prop element :

// App.jsx
import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import NotFound from './pages/NotFound';

function App() {
  return (
    <Routes>
      {/* Route exacte par défaut en v6 */}
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />

      {/* Page 404 — correspond à tout chemin non trouvé */}
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

Le composant <Link> remplace les balises <a> pour la navigation interne sans rechargement :

import { Link } from 'react-router-dom';

function Navbar() {
  return (
    <nav>
      <Link to="/">Accueil</Link>
      <Link to="/about">À propos</Link>
    </nav>
  );
}
À retenir : En v6, le matching est exact par défaut. La prop exact de la v5 a disparu.

Routes imbriquées et Outlet

Les routes imbriquées permettent de partager un layout commun (navbar, sidebar) entre plusieurs pages. Le composant parent déclare ses enfants via <Route> imbriquées, et utilise <Outlet /> pour indiquer où le contenu enfant sera rendu :

// App.jsx — définition des routes imbriquées
import { Routes, Route } from 'react-router-dom';
import DashboardLayout from './layouts/DashboardLayout';
import Overview from './pages/Overview';
import Stats from './pages/Stats';
import Settings from './pages/Settings';

function App() {
  return (
    <Routes>
      <Route path="/dashboard" element={<DashboardLayout />}>
        {/* Route index : rendue sur /dashboard exactement */}
        <Route index element={<Overview />} />
        <Route path="stats" element={<Stats />} />
        <Route path="settings" element={<Settings />} />
      </Route>
    </Routes>
  );
}
// layouts/DashboardLayout.jsx
import { Outlet, NavLink } from 'react-router-dom';

function DashboardLayout() {
  return (
    <div className="dashboard">
      <aside>
        <NavLink to="/dashboard"       end>Vue d'ensemble</NavLink>
        <NavLink to="/dashboard/stats"    >Statistiques</NavLink>
        <NavLink to="/dashboard/settings" >Paramètres</NavLink>
      </aside>
      <main>
        {/* Le composant enfant actif est rendu ici */}
        <Outlet />
      </main>
    </div>
  );
}
Note : <NavLink> ajoute automatiquement la classe active sur le lien correspondant à la route courante. La prop end empêche la route parente de rester active sur les routes enfants.

Navigation programmatique avec useNavigate

Le hook useNavigate permet de naviguer par code, par exemple après la soumission d'un formulaire ou une action utilisateur :

import { useNavigate } from 'react-router-dom';

function LoginForm() {
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    const success = await login(/* credentials */);

    if (success) {
      // Redirection après connexion
      navigate('/dashboard');
    }
  };

  return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

Quelques usages courants de useNavigate :

const navigate = useNavigate();

// Naviguer vers une route
navigate('/profile');

// Passer des données d'état (non visibles dans l'URL)
navigate('/checkout', { state: { fromCart: true } });

// Retour arrière (comme le bouton "précédent" du navigateur)
navigate(-1);

// Remplacer l'entrée courante dans l'historique
navigate('/login', { replace: true });
À retenir : { replace: true } est utile après une connexion ou déconnexion pour éviter un retour arrière vers une page protégée.

Paramètres de route et query strings

Les paramètres dynamiques s'ajoutent avec : dans le chemin, et se récupèrent avec useParams :

// Déclaration dans App.jsx
<Route path="/articles/:id" element={<ArticleDetail />} />
<Route path="/users/:userId/posts/:postId" element={<UserPost />} />
// ArticleDetail.jsx
import { useParams } from 'react-router-dom';

function ArticleDetail() {
  // Correspondance avec :id dans le path
  const { id } = useParams();

  return <h1>Article #{id}</h1>;
}

Pour lire les query strings (?page=2&sort=date), utilisez useSearchParams :

import { useSearchParams } from 'react-router-dom';

function ArticleList() {
  const [searchParams, setSearchParams] = useSearchParams();
  const page = parseInt(searchParams.get('page') || '1', 10);
  const sort = searchParams.get('sort') || 'date';

  const goToNextPage = () => {
    setSearchParams({ page: page + 1, sort });
  };

  return (
    <div>
      <p>Page {page} — Tri : {sort}</p>
      <button onClick={goToNextPage}>Page suivante</button>
    </div>
  );
}

Routes protégées (Private Routes)

Une route protégée redirige l'utilisateur non authentifié vers la page de connexion. En React Router v6, ce pattern s'implémente avec un composant wrapper :

// components/PrivateRoute.jsx
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';

function PrivateRoute() {
  const { isAuthenticated } = useAuth();

  // Si non authentifié, redirection vers /login
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  // Sinon, rendu du composant enfant via Outlet
  return <Outlet />;
}
// App.jsx — utilisation de PrivateRoute
function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />

      {/* Toutes ces routes nécessitent une authentification */}
      <Route element={<PrivateRoute />}>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile"   element={<Profile />} />
        <Route path="/settings"  element={<Settings />} />
      </Route>

      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}
À retenir : La prop replace sur <Navigate> remplace l'entrée courante dans l'historique, évitant que l'utilisateur revienne à la page protégée avec le bouton "précédent".

Lazy loading des routes

Le lazy loading divise votre application en plusieurs bundles JS, chargés à la demande. Cela réduit le temps de chargement initial. Combinez React.lazy(), Suspense et les imports dynamiques :

// App.jsx — lazy loading de toutes les pages
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

// Chaque page est un chunk JS séparé
const Home      = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile   = lazy(() => import('./pages/Profile'));

function App() {
  return (
    // Suspense affiche un loader pendant le chargement du chunk
    <Suspense fallback={<div>Chargement...</div>}>
      <Routes>
        <Route path="/"          element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/profile"   element={<Profile />} />
      </Routes>
    </Suspense>
  );
}

Vite (le bundler recommandé avec React) divise automatiquement les imports dynamiques en chunks distincts, optimisant ainsi le cache navigateur.

Conseil : N'appliquez le lazy loading qu'aux pages et non aux petits composants. Le surcoût réseau d'un chunk supplémentaire peut annuler le bénéfice si le composant est léger.

Conclusion

React Router v6 propose une API cohérente et puissante pour gérer la navigation dans vos SPA React. Les points essentiels à retenir :

  • BrowserRouter + Routes + Route : la base du routage
  • Outlet : rendu des composants enfants dans les layouts imbriqués
  • useNavigate : navigation programmatique sans rechargement
  • useParams + useSearchParams : lecture des paramètres d'URL
  • PrivateRoute avec Navigate : protection des routes authentifiées
  • React.lazy + Suspense : découpage et chargement à la demande
Prochaine étape : Explorez createBrowserRouter (API Data Router de React Router 6.4+) qui ajoute le chargement de données colocalisé avec les routes via les fonctions loader et action.