Rotas aninhadas e layouts
Roteamento cobriu a versão simples da história: uma Route por página, cada uma com seu próprio path e element. Aplicações reais rapidamente crescem além disso, porque as URLs se aninham. Um caminho como /host/income tem duas partes, e frequentemente a página no caminho mais profundo mantém a maior parte da página no caminho mais raso: a mesma navbar, as mesmas abas de seção, com novo conteúdo substituído abaixo. Rotas aninhadas são como React Router expressa isso. Você aninha elementos Route dentro um do outro, e o roteador renderiza toda a cadeia correspondente de uma vez, com parents envolvendo children.
Então "rotas aninhadas" significa duas coisas relacionadas. Os segmentos de URL se aninham, e a interface se aninha com eles: partes da página persistem enquanto você navega mais profundamente, e apenas a parte interna muda. A segunda é o verdadeiro ganho.
Aninhando rotas e renderizando children com Outlet
Route funciona como um elemento self-closing, e também funciona com children. Seus children devem ser outros elementos Route. Quando uma rota child corresponde, React Router renderiza o elemento do parent primeiro, e o parent decide onde o elemento do child aparece renderizando um 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 um buraco no elemento do parent onde o child correspondente é renderizado. Ele faz o mesmo papel que a prop children faz em composição: o parent renderiza a estrutura compartilhada e marca o local onde a parte variável vai. A diferença é que React Router preenche o buraco, escolhendo qual rota child corresponde à URL atual. Sem um Outlet, o elemento do parent toma conta da página e o child correspondente é renderizado em lugar nenhum, que é a primeira coisa a verificar quando uma página aninhada fica em branco.
A rota externa acima é uma rota de layout: tem um element e nenhum path. Uma rota sem caminho corresponde a toda URL que seus children correspondem, então Layout envolve toda página, que é exatamente onde o chrome do site inteiro como header e footer pertence. Um header renderizado ao lado de Routes faz o mesmo trabalho enquanto o aplicativo tem exatamente um layout; rotas de layout são o que deixa uma seção como /host trocar o chrome diferente sem tocar nas páginas abaixo. A opção a evitar é repetir o header dentro de cada componente de página.
Layouts com um caminho
Uma rota de layout também pode carregar um path, que limita a interface compartilhada a uma seção do aplicativo. O projeto VanLife do curso adiciona uma seção /host com sua própria nav secundária, então as páginas do host recebem um segundo layout aninhado dentro do primeiro:
<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 a nav do host mais um Outlet, então em /host/income o roteador renderiza Layout, depois HostLayout em seu outlet, depois Income no outlet do host layout. Três rotas correspondem de uma vez, e cada parent emoldura a próxima. Layouts se empilham tão profundamente quanto o design exigir.
Rotas index
Há uma lacuna nessa configuração: HostLayout renderiza em /host, mas seu outlet não tem nada para mostrar lá, porque cada child adiciona um segmento ao caminho. A solução é uma rota index: substitua path pela prop index, e aquele child renderiza no outlet do parent no seu próprio caminho.
<Route index element={<Dashboard />} />Agora /host mostra a nav do host com o dashboard dentro dela, e /host/income troca o dashboard pela página de income. Pense em index como "o child padrão": o que pertence ao outlet antes de qualquer segmento mais profundo ser adicionado. Funciona no nível superior também, onde <Route index element={<Home />} /> é a página inicial dentro do layout raiz, a mesma rota que o exemplo de abertura escreveu como path="/".
Caminhos relativos e links relativos
Note os caminhos dos children acima: income, sem barra inicial. Um caminho que começa com / é absoluto, medido a partir da raiz do site. Um caminho sem ele é relativo à sua rota parent, então o income aninhado significa /host/income. Isso mantém árvores de rotas profundas mantíveis: renomeie host para admin em um lugar e todo caminho descendente segue.
Links recebem o mesmo tratamento. Um Link renderizado dentro de um elemento roteado resolve caminhos relativos contra a rota que o renderizou, então dentro de HostLayout a nav pode droppar o prefixo /host inteiramente:
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 />
</>
)
}Dois valores especiais pegam emprestado seu significado de caminhos de sistema de arquivos. to="." linka para a rota atual, que é como o link Dashboard targeta /host sem hardcodá-lo. to=".." sobe para cima, útil para links "voltar para a lista"; ele vem com uma sutileza, já que sobe uma rota em vez de um segmento de URL, o que começa a importar uma vez que uma única rota possua vários segmentos, e Route params and location o coloca em prática em um botão de voltar real.
NavLink e ancestor matching
Troque aqueles elementos Link por NavLink para estilizar a página atual, como o curso faz para a nav do host, e ancestor matching entra em jogo: o roteador conta todo ancestor da URL atual como correspondente, então o link Dashboard to="." relata como ativo em /host/income e /host/reviews também. Adicione end àquele link e ele permanece sem estilo em qualquer lugar abaixo de /host.
Passando dados para baixo com useOutletContext
Um layout frequentemente possui dados que seus children precisam. HostVanDetail no curso busca uma van e a mantém em state, depois renderiza uma nav de abas e um Outlet onde a página de info, pricing ou photos aparece. Props não podem alcançar através de um Outlet, já que o layout nunca renderiza aqueles componentes diretamente. A resposta do React Router é a prop context em Outlet, lida do outro lado com useOutletContext:
<Outlet context={{ currentVan }} />import { useOutletContext } from 'react-router-dom'
export default function HostVanInfo() {
const { currentVan } = useOutletContext()
return <h4>Name: {currentVan.name}</h4>
}Qualquer valor que você passe para context é o que useOutletContext retorna em qualquer child que preencha atualmente o outlet. Passar um objeto e fazer destructuring no lado receptor é a forma comum, e deixa espaço para adicionar mais valores depois. Se Context é familiar, isso se parecerá familiar também: o outlet atua como um provider limitado a exatamente um buraco na página.
Aninhar ou não aninhar
Aninhamento se justifica quando rotas compartilham interface. Esse é o teste. A URL sozinha pode tentá-lo a aninhar: /vans e /vans/:id compartilham um segmento, então por que não compartilhar uma rota? Mas em VanLife aquelas duas páginas não compartilham nada visível além do chrome do site inteiro, e aninhá-las força uma forma desajeitada: uma rota parent com um path e nenhum element, que volta para renderizar um Outlet vazio, mais um child index para renderizar a lista. Essa é mais estrutura para zero benefício.
Quando há interface compartilhada para manter na tela, aninhe e dê ao parent um elemento de layout com um Outlet. Quando a única motivação é evitar um segmento de caminho repetido, duas rotas flat são a escolha mais simples e clara.
Version note
The examples here import from react-router-dom, which works in both v6 and v7; Routing covers what v7 changed about the packages. The nested-route concepts are identical in both versions.
Outlet onde a página child deve aparecer. Uma rota com um element e nenhum path é um layout que envolve tudo, e uma rota index é o child padrão mostrado no endereço do parent.
Se duas páginas não compartilham nada na tela, não há necessidade de aninhá-las.
Next up: Route params and location, where a single route serves many pages.

