Rutas anidadas y layouts
Routing cubrió la versión plana de la historia: una Route por página, cada una con su propio path y element. Las aplicaciones reales rápidamente superan eso, porque las URLs se anidan. Una ruta como /host/income tiene dos partes, y a menudo la página en la ruta más profunda mantiene la mayor parte de la página en la ruta más superficial: la misma navbar, las mismas pestañas de sección, con contenido nuevo intercalado abajo. Las rutas anidadas son cómo React Router expresa eso. Anidas elementos Route uno dentro de otro, y el router renderiza toda la cadena coincidente de una vez, los padres envolviendo a los hijos.
Entonces "rutas anidadas" significa dos cosas relacionadas. Los segmentos de URL se anidan, y la interfaz se anida con ellos: partes de la página persisten mientras navegas más profundamente, y solo la parte interna cambia. El segundo es el beneficio real.
Anidar rutas y renderizar hijos con Outlet
Route funciona como un elemento de cierre automático, y también funciona con hijos. Sus hijos deben ser otros elementos Route. Cuando una ruta hija coincide, React Router renderiza primero el elemento del padre, y el padre decide dónde aparece el elemento del hijo renderizando un Outlet.
import { BrowserRouter, Routes, Route } from 'react-router-dom'
function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="vans" element={<Vans />} />
</Route>
</Routes>
</BrowserRouter>
)
}import { Outlet } from 'react-router-dom'
import Header from './Header'
export default function Layout() {
return (
<>
<Header />
<Outlet />
</>
)
}Outlet abre un agujero en el elemento del padre donde el hijo coincidente se renderiza. Juega el mismo papel que la prop children en composición: el padre renderiza la estructura compartida y marca el lugar donde va la parte variable. La diferencia es que React Router llena el agujero, eligiendo cuál ruta hija coincide con la URL actual. Sin un Outlet, el elemento del padre toma el control de la página y el hijo coincidente no se renderiza en ningún lado, que es lo primero que debes revisar cuando una página anidada sale en blanco.
Esa ruta externa de arriba es una ruta de layout: tiene un element y ningún path. Una ruta sin path coincide con cada URL que sus hijos coinciden, así que Layout envuelve cada página, que es exactamente dónde pertenece el chrome de sitio como un encabezado y pie de página. Un encabezado renderizado junto a Routes hace el mismo trabajo mientras la app tiene exactamente un layout; las rutas de layout son lo que permite a una sección como /host intercambiar chrome diferente sin tocar las páginas debajo. La opción a evitar es repetir el encabezado dentro de cada componente de página.
Layouts con un path
Una ruta de layout también puede llevar un path, que limita la interfaz compartida a una sección de la app. El proyecto VanLife del curso agrega una sección /host con su propia nav secundaria, así que las páginas del host obtienen un segundo layout anidado dentro del primero:
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="host" element={<HostLayout />}>
<Route path="income" element={<Income />} />
<Route path="reviews" element={<Reviews />} />
</Route>
</Route>HostLayout renderiza la nav del host más un Outlet, así que en /host/income el router renderiza Layout, luego HostLayout en su outlet, luego Income en el outlet del host layout. Tres rutas coinciden a la vez, y cada padre enmarca el siguiente. Los layouts se apilan tan profundamente como el diseño lo requiera.
Rutas index
Hay una brecha en esa configuración: HostLayout se renderiza en /host, pero su outlet no tiene nada que mostrar allí, porque cada hijo suma un segmento a la ruta. La solución es una ruta index: reemplaza path con la prop index, y ese hijo se renderiza en el outlet del padre en la ruta del padre mismo.
<Route index element={<Dashboard />} />Ahora /host muestra la nav del host con el dashboard adentro, y /host/income intercambia el dashboard por la página de ingresos. Piensa en index como "el hijo por defecto": qué pertenece en el outlet antes de que se agregue algún segmento más profundo. También funciona en el nivel superior, donde <Route index element={<Home />} /> es la página de inicio dentro del layout raíz, la misma ruta que el ejemplo de apertura escribió como path="/".
Rutas relativas y enlaces relativos
Observa los paths de los hijos arriba: income, sin barra diagonal inicial. Un path que comienza con / es absoluto, medido desde la raíz del sitio. Un path sin uno es relativo a su ruta padre, así que el income anidado significa /host/income. Esto mantiene los árboles de rutas profundas mantenibles: renombra host a admin en un lugar y cada ruta descendiente lo sigue.
Los enlaces reciben el mismo tratamiento. Un Link renderizado dentro de un elemento enrutado resuelve paths relativos contra la ruta que lo renderizó, así que dentro de HostLayout la nav puede soltar el prefijo /host completamente:
import { Link, Outlet } from 'react-router-dom'
export default function HostLayout() {
return (
<>
<nav>
<Link to=".">Dashboard</Link>
<Link to="income">Income</Link>
<Link to="reviews">Reviews</Link>
</nav>
<Outlet />
</>
)
}Dos valores especiales toman su significado de los paths del sistema de archivos. to="." enlaza a la ruta actual, que es cómo el enlace Dashboard apunta a /host en sí mismo sin codificarlo. to=".." sube hacia arriba, útil para enlaces "volver a la lista"; viene con una sutileza, ya que sube una ruta en lugar de un segmento de URL, que comienza a importar una vez que una sola ruta posee varios segmentos, y Route params and location lo pone en funcionamiento en un botón atrás real.
NavLink y coincidencia de ancestros
Intercambia esos elementos Link por NavLink para estilizar la página actual, como el curso hace para la nav del host, y entra en juego la coincidencia de ancestros: el router cuenta cada ancestro de la URL actual como coincidente, así que el enlace Dashboard to="." reporta activo en /host/income y /host/reviews también. Agrega end a ese único enlace y se mantiene sin estilo en cualquier lugar debajo de /host.
Pasar datos hacia abajo con useOutletContext
Un layout a menudo posee datos que sus hijos necesitan. HostVanDetail en el curso obtiene una van y la mantiene en estado, luego renderiza una nav de pestañas y un Outlet donde aparece la página de info, pricing o fotos. Los props no pueden llegar a través de un Outlet, ya que el layout nunca renderiza esos componentes directamente. La respuesta de React Router es la prop context en Outlet, leída del otro lado con useOutletContext:
<Outlet context={{ currentVan }} />import { useOutletContext } from 'react-router-dom'
export default function HostVanInfo() {
const { currentVan } = useOutletContext()
return <h4>Name: {currentVan.name}</h4>
}Sea cual sea el valor que pases a context es lo que useOutletContext devuelve en el hijo que actualmente llena el outlet. Pasar un objeto y desestructurar en el extremo receptor es la forma común, y deja espacio para agregar más valores después. Si Context te es familiar, esto se sentirá familiar también: el outlet actúa como un proveedor limitado a exactamente un agujero en la página.
Anidar o no anidar
Anidar vale la pena cuando las rutas comparten interfaz. Esa es la prueba. La URL sola puede tentarte a anidar: /vans y /vans/:id comparten un segmento, ¿por qué no compartir una ruta? Pero en VanLife esas dos páginas no comparten nada visible más allá del chrome de sitio, y anidarlas fuerza una forma incómoda: una ruta padre con un path y ningún element, que retrocede a renderizar un Outlet desnudo, más un hijo index para renderizar la lista. Esa es más estructura para cero beneficio.
Cuando hay interfaz compartida para mantener en pantalla, anida y dale al padre un elemento de layout con un Outlet. Cuando la única motivación es evitar un segmento de path repetido, dos rutas planas son la opción más simple y clara.
Nota de versión
Los ejemplos aquí importan de react-router-dom, que funciona en v6 y v7; Routing cubre qué cambió v7 sobre los paquetes. Los conceptos de rutas anidadas son idénticos en ambas versiones.
Outlet donde debe aparecer la página hija. Una ruta con un element y sin path es un layout que envuelve todo, y una ruta index es el hijo por defecto mostrado en la dirección del padre mismo.
Si dos páginas no comparten nada en pantalla, no hay necesidad de anidarlas.
Next up: Route params and location, where a single route serves many pages.

