Routage avec React Router
Le concept de route
Une route est une association entre une URL et une ressource (page, composant, vue). Dans une application web traditionnelle, chaque URL correspond à une requête HTTP : le navigateur demande une page complète au serveur, qui la renvoie. Chaque clic sur un lien recharge donc toute la page.

Une Single Page Application (SPA) charge un unique document HTML, puis met à jour dynamiquement son contenu via JavaScript quand l’utilisateur interagit. La navigation entre « pages » se fait sans rechargement : seule la portion concernée de l’interface est re-rendue.
| Application multi-pages (MPA) | Single Page Application (SPA) | |
|---|---|---|
| Rendu | Côté serveur, à chaque requête | Côté client, après premier chargement |
| Changement de page | Rechargement complet | Mise à jour partielle |
| Premier chargement | Rapide | Plus lent (télécharge l’application entière) |
| Navigation suivante | Nouvelle requête | Quasi-instantanée |
Dans une SPA React, le routeur gère cette navigation côté client : il observe l’URL, et pour chaque URL il décide quel composant afficher.
React Router
React Router est la bibliothèque de routage de référence pour React. Elle permet de déclarer des routes, de naviguer entre elles et d’accéder aux paramètres d’URL depuis n’importe quel composant.
Installation
npm install react-router-domConfiguration de base
Enveloppez votre application dans un <BrowserRouter> au point d’entrée :
import { StrictMode } from 'react';import { createRoot } from 'react-dom/client';import { BrowserRouter } from 'react-router-dom';import App from './App';
createRoot(document.getElementById('root')).render( <StrictMode> <BrowserRouter> <App /> </BrowserRouter> </StrictMode>);Puis déclarez vos routes avec <Routes> et <Route> :
import { Routes, Route } from 'react-router-dom';import Accueil from './pages/Accueil';import APropos from './pages/APropos';
function App() { return ( <Routes> <Route path="/" element={<Accueil />} /> <Route path="/a-propos" element={<APropos />} /> </Routes> );}
export default App;<Routes> examine l’URL courante et affiche la première route qui correspond (et seulement celle-là).
Naviguer entre les routes
<Link> : le remplaçant de <a>
Pour créer un lien interne, utilisez <Link> plutôt que <a href> :
import { Link } from 'react-router-dom';
function Menu() { return ( <nav> <Link to="/">Accueil</Link> <Link to="/a-propos">À propos</Link> </nav> );}<NavLink> : un lien qui connaît son état
<NavLink> se comporte comme <Link>, mais ajoute automatiquement une classe active quand l’URL courante correspond à son to. Pratique pour mettre en valeur l’onglet de navigation actuellement affiché :
import { NavLink } from 'react-router-dom';
function Menu() { return ( <nav> <NavLink to="/" className={({ isActive }) => isActive ? 'actif' : ''} > Accueil </NavLink> <NavLink to="/a-propos" className={({ isActive }) => isActive ? 'actif' : ''} > À propos </NavLink> </nav> );}Les routes dynamiques
Quand plusieurs URLs partagent la même structure (une par alerte, une par utilisateur, etc.), on utilise une route dynamique avec un paramètre d’URL précédé de : :
<Routes> <Route path="/" element={<Accueil />} /> <Route path="/alertes/:id" element={<DetailAlerte />} /></Routes>:id est un segment variable. Les URLs /alertes/42, /alertes/abc, /alertes/2026-04-15 correspondent toutes à cette route.
Lire le paramètre avec useParams
Dans le composant cible, on récupère la valeur du paramètre avec le hook useParams :
import { useParams } from 'react-router-dom';import { useEffect, useState } from 'react';import { getAlerteById } from '../services/alertes';
function DetailAlerte() { const { id } = useParams(); const [alerte, setAlerte] = useState(null);
useEffect(() => { getAlerteById(id).then(setAlerte); }, [id]);
if (!alerte) return <p>Chargement…</p>;
return ( <article> <h1>{alerte.titre}</h1> <p>{alerte.description}</p> </article> );}Générer des liens dynamiques
<Link to={`/alertes/${alerte.id}`}> {alerte.titre}</Link>La navigation programmatique avec useNavigate
Parfois, on doit naviguer en réaction à un évènement qui n’est pas un clic sur un lien : soumission de formulaire, bouton « retour », redirection après connexion, etc. On utilise alors le hook useNavigate :
import { useNavigate } from 'react-router-dom';
function BoutonRetour() { const navigate = useNavigate();
return ( <button onClick={() => navigate(-1)}> ← Retour </button> );}navigate accepte :
- Une chaîne :
navigate('/')ounavigate('/alertes/42') - Un entier :
navigate(-1)revient en arrière,navigate(1)avance (comme les flèches du navigateur) - Une option :
navigate('/', { replace: true })remplace l’entrée dans l’historique au lieu d’en ajouter une (utile après connexion).
Les paramètres de recherche (useSearchParams)
Pour stocker des filtres dans l’URL (afin qu’elle soit partageable), on utilise la chaîne de requête (?sujet=Eau&q=travaux) et le hook useSearchParams :
import { useSearchParams } from 'react-router-dom';
function BarreRecherche() { const [searchParams, setSearchParams] = useSearchParams(); const recherche = searchParams.get('q') ?? '';
return ( <input value={recherche} onChange={(e) => setSearchParams({ q: e.target.value })} placeholder="Rechercher…" /> );}Avantage : l’URL https://monapp.ca/?q=piscine est copiable, partageable, et rafraîchissable. L’état de recherche est préservé.
La route 404
Pour attraper toutes les URLs qui ne correspondent à aucune route définie, ajoutez une route avec le motif * en dernier :
<Routes> <Route path="/" element={<Accueil />} /> <Route path="/alertes/:id" element={<DetailAlerte />} /> <Route path="*" element={<PageIntrouvable />} /></Routes>import { Link } from 'react-router-dom';
function PageIntrouvable() { return ( <div> <h1>404 — Page introuvable</h1> <Link to="/">Retour à l'accueil</Link> </div> );}Les mises en page partagées avec <Outlet>
Plutôt que de dupliquer l’entête dans chaque page, on définit une route « parente » qui contient un <Outlet> à l’emplacement du contenu variable :
import { Outlet } from 'react-router-dom';import Entete from './Entete';import PiedDePage from './PiedDePage';
function Layout() { return ( <> <Entete /> <main> <Outlet /> </main> <PiedDePage /> </> );}
export default Layout;Puis on imbrique les routes sous cette mise en page :
<Routes> <Route element={<Layout />}> <Route path="/" element={<Accueil />} /> <Route path="/alertes/:id" element={<DetailAlerte />} /> </Route> <Route path="*" element={<PageIntrouvable />} /></Routes>Pour les routes imbriquées, <Outlet> est remplacé à l’exécution par le composant de la route enfant correspondante.
L’entête et le pied de page restent en place ; seul le <main> change.
Organisation d’un projet React Router
Une structure courante et saine pour un projet de la taille du vôtre :
src/├── components/ Composants réutilisables (UI)│ ├── Entete.jsx│ ├── Layout.jsx│ └── CarteAlerte.jsx├── pages/ Une page = une route│ ├── Accueil.jsx│ ├── DetailAlerte.jsx│ └── PageIntrouvable.jsx├── services/ Accès aux données│ └── alertes.js├── App.jsx Déclaration des routes└── main.jsx Point d'entrée + <BrowserRouter>Dans cette convention :
/pagescontient les composants qui correspondent à une route./componentscontient des composants réutilisables (boutons, cartes, entête, etc.)./servicescontient la logique d’accès aux données.
Erreurs communes à éviter
| Erreur | Symptôme | Correction |
|---|---|---|
Utiliser <a href="/..."> pour la navigation interne | Rechargement complet, perte d’état | Utiliser <Link to="/..."> |
Oublier d’envelopper dans <BrowserRouter> | « useRoutes may be used only in the context of a Router » | Ajouter <BrowserRouter> autour de <App /> dans main.jsx |
Mettre la route * avant les autres | Toutes les URLs tombent sur la 404 | Placer la route * en dernier dans <Routes> |
Utiliser useHistory (v5) | Erreur : export introuvable | Utiliser useNavigate (v6+) |
Utiliser <Switch> (v5) | Erreur : export introuvable | Utiliser <Routes> (v6+) |
Oublier : devant un paramètre d’URL (/alertes/id) | useParams() retourne un objet vide | Écrire path="/alertes/:id" |
Ne pas mettre id dans le tableau de dépendances de useEffect | Les données ne se rafraîchissent pas quand on passe d’une alerte à l’autre | Inclure [id] |
Récapitulatif
- Une SPA gère la navigation côté client : l’URL change, mais la page ne se recharge pas.
- React Router associe chaque URL à un composant via
<Routes>et<Route>. <Link>/<NavLink>remplacent<a href>pour la navigation interne.- Les routes dynamiques (
/alertes/:id) etuseParamspermettent de gérer des ressources individuelles. useNavigatepermet la navigation programmatique (retour, redirection après action).useSearchParamssynchronise l’état d’une recherche ou d’un filtre avec la chaîne de requête de l’URL.<Outlet>permet de partager une mise en page entre plusieurs routes.- Une route
path="*"en dernier attrape toutes les URLs non reconnues (404).
Exercices
- Menu principal. Créez un composant
Menuqui affiche trois<NavLink>(Accueil, À propos, Contact) et applique une classe CSSactifau lien courant. - Route dynamique. À partir d’une liste d’arrondissements
{ id, nom }, créez une page d’accueil listant chaque arrondissement sous forme de lien, et une page/arrondissements/:idqui affiche le nom correspondant viauseParams. - Bouton retour. Sur la page de détail, ajoutez un bouton qui revient à la page précédente via
useNavigate(-1). Que se passe-t-il si l’utilisateur arrive directement sur cette URL (copier-coller) ? - Filtres dans l’URL. Créez un composant
FiltreSujetqui utiliseuseSearchParamspour stocker le sujet sélectionné dans l’URL (?sujet=Eau). Rafraîchissez la page : la sélection est préservée. - Mise en page partagée. Refactorisez une petite application à deux pages pour utiliser
<Outlet>et une mise en page commune (entête + pied de page).