Hasta ahora, nuestras rutas funcionaban como un “interruptor”: o se muestra el Componente A, o se muestra el Componente B.
Pero las interfaces web modernas no son tan simples. A menudo tenemos una estructura de cajas dentro de cajas.
Pensad en un Panel de Administración (Dashboard).
- Tenéis una barra lateral (Sidebar) a la izquierda que siempre está ahí.
- Tenéis una barra superior (Header) que siempre está ahí.
- Y solo cambia el contenido central cuando navegáis entre “Perfil”, “Ajustes” o “Estadísticas”.
Si hicierais esto como hemos aprendido hasta ahora, tendríais que repetir el <Sidebar /> y el <Header /> dentro de cada página. Eso duplica código y, peor aún, desmonta y monta esos componentes en cada navegación, perdiendo su estado (como un texto en la barra de búsqueda).
La solución son las Rutas Anidadas (Nested Routes) y el componente que deja el hueco: Outlet.
El concepto: Layouts y “Agujeros”
Pensad en un Layout como una plantilla maestra. Es un componente que pinta la estructura común (Navbar, Footer, Sidebar) y deja un “hueco” reservado donde se pintará el contenido específico de la ruta hija.
Ese “hueco” en React Router se llama <Outlet />.
Vamos a crear un componente que servirá de envoltorio.
// src/layouts/MainLayout.jsx
import { Outlet, Link } from 'react-router';
export default function MainLayout() {
return (
<div className="layout-principal">
{/* 1. Elementos fijos (Persistentes) */}
<nav>
<Link to="/">Home</Link> | <Link to="/blog">Blog</Link>
</nav>
<hr />
{/* 2. El hueco donde se renderizarán las rutas hijas */}
<main>
<Outlet />
</main>
{/* 3. Footer fijo */}
<footer>© 2024 Mi Web</footer>
</div>
);
}
Si no ponéis el componente <Outlet />, las rutas hijas no se renderizarán nunca. Es el error más común al empezar.
Ahora vamos a App.jsx. En lugar de poner las rutas una detrás de otra, vamos a meter unas dentro de otras.
// src/App.jsx
import { Routes, Route } from 'react-router';
import MainLayout from './layouts/MainLayout';
import Home from './pages/Home';
import Blog from './pages/Blog';
function App() {
return (
<Routes>
{/* Ruta Padre: El Layout */}
<Route path="/" element={<MainLayout />}>
{/* Rutas Hijas (Nested Routes) */}
{/* Index Route: Se muestra cuando la URL es exactamente "/" */}
<Route index element={<Home />} />
{/* Path relativo: Se suma al del padre ("/blog") */}
<Route path="blog" element={<Blog />} />
</Route>
</Routes>
);
}
¿Qué ocurre aquí?
- Cuando el usuario entra en
/, React Router ve que coincide con el padre (MainLayout). - Renderiza
<MainLayout />. - Dentro del Layout, encuentra el
<Outlet />. - Como la URL es
/, busca la ruta hija marcada comoindex(Home). - Inyecta
<Home />dentro del<Outlet />.
Si el usuario navega a /blog:
<MainLayout />se mantiene intacto (no se desmonta).- React Router cambia lo que hay en el
<Outlet />: quita<Home />y pone<Blog />.
Múltiples layouts
La gracia de esto es que podemos tener varios layouts en la misma aplicación.
Por ejemplo, vuestra web puede tener una parte pública (con Navbar normal) y una zona privada de administración (con Sidebar y colores oscuros).
<Routes>
{/* ZONA PÚBLICA */}
<Route path="/" element={<PublicLayout />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
</Route>
{/* ZONA PRIVADA (Dashboard) */}
{/* Todas las rutas empezarán por /app/ ... */}
<Route path="/app" element={<DashboardLayout />}>
<Route index element={<Resumen />} /> {/* /app */}
<Route path="perfil" element={<Perfil />} /> {/* /app/perfil */}
<Route path="ajustes" element={<Ajustes />} /> {/* /app/ajustes */}
</Route>
</Routes>
Aquí, DashboardLayout tendrá su propia estructura (quizás un Sidebar lateral) y su propio <Outlet>.
