
Actuellement, notre application Shiny fonctionne très bien : nous avons des pages pour le Dashboard, les Employés et les Projets, ainsi que des pages de détails pour chaque employé et chaque projet. Félicitations !
Si vous regardez maintenant le fichier main.jsx , vous remarquerez que le composant Header est placé en dehors du Routes. Cette structure permet d’afficher le même header sur toutes les pages de l’application.
Mais cette approche montre rapidement ses limites. Que se passerait-il si certaines sections de l’application devaient avoir une structure différente ? Par exemple, une partie publique avec un Header simple, et une partie d’administration avec un Header et une sidebar.
La réponse : les routes imbriquées, associées au composantOutlet !
Dans ce chapitre, nous allons restructurer notre application afin de définir des layouts différents selon les sections, tout en conservant une navigation maintenable et flexible. Alors, partons à la découverte des routes imbriquées !
Les routes imbriquées (nested routes en anglais) permettent de créer une hiérarchie de routes : une route parent contient plusieurs routes enfants. C'est comme un dossier qui contient des sous-dossiers dans votre système de fichiers.
Prenons un exemple concret. Imaginez une section "À propos" avec plusieurs sous-pages :
/about # → Présentation générale
/about/team # → L'équipe
/about/values # → Nos valeurs
/about/contact # → ContactToutes ces routes partagent le même préfixe /about et pourraient partager le même layout (une navigation secondaire, par exemple). Les routes imbriquées permettent d'organiser ce genre de structure de manière élégante.
Ouvrez votre fichier main.jsx. Vous verrez cette structure :
<Router>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/employees" element={<Employees />} />
<Route path="/employees/:id" element={<EmployeeDetail />} />
{/* ... */}
</Routes>
</Router>Le Header est en dehors du système de routing. Cela signifie qu'il est toujours affiché, sur toutes les pages. C'est bien pour notre application actuelle, mais c'est limitant :
Impossible d'avoir des pages sans Header
Impossible d'avoir des Headers différents selon les sections
Le Header n'est pas considéré comme faisant partie du layout par React Router
Comment résoudre ça ?
Avec le composant Outlet !
Le composant Outlet est un emplacement dans lequel React Router affiche le contenu de la route enfant qui correspond à l’URL courante.
Concrètement, une route parente définit souvent un layout (par exemple un header, une sidebar, etc.), et les routes enfants définissent le contenu qui change à l’intérieur de ce layout.
Imaginez un cadre de tableau : le cadre représente le layout (défini par la route parente), et l’image à l’intérieur correspond à la route enfant affichée dans Outlet.
Outlet est importé depuis react-router-dom:
import { Outlet } from 'react-router-dom'Nous allons créer un composant Layout qui contient le Header et un Outlet pour le contenu.
Créez le dossier /src/components/Layout et ajoutez-y le fichierindex.jsx :
import { Outlet } from 'react-router-dom'
import Header from '../Header'
import './Layout.css'
function Layout() {
return (
<div className="layout">
<Header />
<main className="layout-content">
<Outlet />
</main>
</div>
)
}
export default Layout
Explications :
Ligne 1 : Import du composant Outlet
Ligne 8 : Le Header est affiché en haut
Ligne 10 : L'Outlet affichera le contenu de la route enfant active
Créez maintenant le fichier Layout.css dans le même dossier :
.layout {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.layout-content {
flex: 1;
}
Ces styles assurent que le layout prend toute la hauteur de l'écran et que le contenu principal occupe l'espace disponible.
Maintenant, transformons notre router pour utiliser le Layout comme route parent.
Ouvrez main.jsx et importez Layout :
import Layout from './components/Layout'Puis modifiez la structure des routes. Maintenant, nous avons :
<Router>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/projects" element={<Projects />} />
<Route path="/projects/:id" element={<ProjectDetail />} />
<Route path="/employees" element={<Employees />} />
{/* .... */}
</Route>
</Routes>
</Router>Les changements :
Ligne 2 : Header retiré (il est maintenant dans Layout)
Ligne 3 : Route parent avec element={<Layout />} mais sans path
Lignes 4-10 : Toutes les routes sont imbriquées dans la route Layout
Testez votre application avec npm run dev. Tout fonctionne exactement comme avant, mais le code est maintenant mieux structuré ! Le Header fait partie du système de routing.
Notre Header fonctionne bien, mais il manque un élément important pour l'expérience utilisateur : l'indication visuelle de la page active. Actuellement, impossible de savoir sur quelle page on se trouve juste en regardant la navigation.
React Router fournit le composant NavLink, qui est comme Link mais avec une fonctionnalité supplémentaire : il ajoute automatiquement la classe CSS active au lien qui correspond à la route actuelle.
Importez-le dans votre Header :
import { NavLink } from 'react-router-dom'Puis remplacez tous vos Link par des NavLink :
<nav className="nav">
<NavLink to="/" className="nav-link">Dashboard</NavLink>
<NavLink to="/projects" className="nav-link">Projets</NavLink>
<NavLink to="/employees" className="nav-link">Employés</NavLink>
<NavLink to="/about" className="nav-link">À propos</NavLink>
</nav>Maintenant, ajoutez le style pour les liens actifs dans Header.css :
.nav-link.active {
color: #646cff;
font-weight: 700;
border-bottom: 3px solid #646cff;
}Testez dans votre navigateur : le lien de la page actuelle est maintenant mis en évidence ! C'est beaucoup plus clair pour l'utilisateur.
Petit problème : Si vous cliquez sur "Dashboard" puis "Employés", vous remarquerez que "Dashboard" reste actif.
Pourquoi ?
Parce que / est inclus dans/employees. React Router considère que / est actif pour toutes les routes.
La solution : ajoutez la prop end au NavLink du Dashboard :
<Navlink to="/" end="" classname="nav-link">Dashboard</Navlink>La propend indique que le lien n'est actif que si l'URL correspond exactement au path, sans rien après. Problème résolu !
Maintenant que vous maîtrisez les bases, voyons comment créer une vraie section avec routes imbriquées.
Actuellement, /about affiche une simple page.
Et si nous voulions plusieurs sous-pages ?
/about → Présentation de Shiny
/about/team → L'équipe
/about/contact → Contact
Créez trois fichiers dans /src/pages/About/ :
Presentation.jsx :
function Presentation() {
return (
<div className="page">
<h1>À propos de Shiny ✨</h1>
<p>Shiny est une entreprise innovante spécialisée dans le développement web.</p>
</div>
)
}
export default Presentation
Team.jsx :
function Team() {
return (
<div className="page">
<h1>Notre équipe 👥</h1>
<p>Une équipe passionnée de 10 personnes.</p>
</div>
)
}
export default Team
Contact.jsx :
function Contact() {
return (
<div className="page">
<h1>Contactez-nous 📧</h1>
<p>Email : contact@shiny.com</p>
</div>
)
}
export default Contact
Dans main.jsx, importez les nouveaux composants :
import AboutPresentation from './pages/About/Presentation'
import AboutTeam from './pages/About/Team'
import AboutContact from './pages/About/Contact'Puis modifiez la route /about pour la transformer en route parent :
<Route path="/about" element={<AboutLayout />}>
<Route index element={<AboutPresentation />} />
<Route path="team" element={<AboutTeam />} />
<Route path="contact" element={<AboutContact />} />
</Route>Nouveauté : <Route index>
La route index est la route par défaut quand on accède au path parent. Ici, /about afficheraAboutPresentation. C'est équivalent à path="" mais plus explicite et recommandé.
Créez /src/pages/About/Layout.jsx :
import { Outlet, NavLink } from 'react-router-dom'
import './AboutLayout.css'
function AboutLayout() {
return (
<div className="page">
<nav className="about-nav">
<NavLink to="/about" end className="about-link">Présentation</NavLink>
<NavLink to="/about/team" className="about-link">Équipe</NavLink>
<NavLink to="/about/contact" className="about-link">Contact</NavLink>
</nav>
<div className="about-content">
<Outlet />
</div>
</div>
)
}
export default AboutLayout
Créez le CSS AboutLayout.css :
.about-nav {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
padding-bottom: 1rem;
}
.about-link {
padding: 0.5rem 1rem;
color: #ccc;
text-decoration: none;
border-radius: 4px;
transition: all 0.2s ease;
}
.about-link.active {
background: #646cff;
color: #fff;
}
Nous avons maintenant une nouvelle navigation pour la section À propos de notre site !

Testez dans votre navigateur : vous avez maintenant une section complète avec navigation secondaire ! Cliquez sur "Équipe" ou "Contact", la navigation secondaire reste affichée et le contenu change. C'est exactement le comportement d'une application professionnelle.
Structure finale :
Layout # (Header global)
└─ /about # → AboutLayout (navigation secondaire)
├─ index # → Presentation
├─ team # → Team
└─ contact # → ContactVous avez deux niveaux d'imbrication : le Layout global contient AboutLayout, qui contient les pages de contenu. Chaque niveau a son propre Outlet qui affiche le niveau suivant. C'est la puissance des routes imbriquées !

C'est maintenant le moment de voler de vos propres ailes et d'appliquer ces concepts par vous-même ! Vous retrouverez le code dans la branche P1C4-Begin
Contexte : Actuellement /projects affiche tous les projets. Vous voulez ajouter des filtres par statut avec une navigation secondaire.
Objectifs :
Créer un ProjectsLayout
Créez /src/pages/Projects/Layout.jsx
Ajoutez une navigation secondaire avec NavLink : "Tous", "En cours", "Terminés", "Planifiés"
Ajoutez un Outlet pour le contenu
Stylisez la navigation avec ProjectsLayout.css
Créer les pages de filtres
/src/pages/Projects/All.jsx → Tous les projets
/src/pages/Projects/InProgress.jsx → Projets "En cours"
/src/pages/Projects/Completed.jsx → Projets "Terminés"
/src/pages/Projects/Planned.jsx → Projets "Planifiés"
Configurer les routes dans main.jsx
Transformez la route /projects en route parent avec ProjectsLayout
Ajoutez les routes enfants : index (All),in-progress, completed, planned
Importez tous les composants nécessaires
Filtrer les données
Utilisez getProjectsByStatus() déjà disponible dans data/projects.js
Dans chaque composant, récupérez les projets filtrés
Affichez-les avec le composant ProjectCard
Utiliser NavLink correctement
Ajoutez end sur le lien "Tous" pour éviter qu'il soit toujours actif
Stylisez les liens actifs différemment
Découvrez la solution sur la branche P1C4-Solution-1
Objectif avancé : Ajoutez un fil d'Ariane (breadcrumb) dans le Layout qui affiche le chemin de navigation.
Exemple :
Sur /employees → "Dashboard / Employés"
Sur /employees/5 → "Dashboard / Employés / Sophie Martin"
Sur /about/team → "Dashboard / À propos / Équipe"
La solution se trouve dans la branche P1C4-Solution-2.
Les routes imbriquées permettent de créer une hiérarchie de routes avec des routes parents et enfants.
Le composant Outlet affiche le contenu de la route enfant correspondante dans le layout parent.
Les layouts partagés (comme notre composant Layout ) évitent la répétition de code et structurent l'application.
Une route index (<Route index>) est la route par défaut d'une route parent, équivalent à path="".
NavLink ajoute automatiquement la classe CSS active au lien actif pour améliorer l'expérience utilisateur.
La prop end sur NavLink permet de contrôler précisément quand un lien est considéré actif.
Vous maîtrisez maintenant l'organisation avancée de votre application avec React Router ! Votre code est plus structuré, plus maintenable, et votre application offre une meilleure expérience utilisateur.
Félicitations ! Vous avez terminé ce cours sur les bases de React Router. Il est temps maintenant de valider les compétences acquises au travers d'un quiz !