React Router: rotas, links e parâmetros na URL
Como configurar rotas no React Router 7, navegar sem recarregar a página, ler parâmetro da URL e proteger uma rota que exige login.
Sem rotas, uma aplicação React pode até trocar de tela, mas a pessoa não consegue copiar o endereço daquela tela, atualizar a página no mesmo ponto nem usar o botão voltar como espera. O React Router resolve justamente isso: ele relaciona cada URL a um componente e mantém endereço, conteúdo e histórico caminhando juntos.
Neste artigo você vai montar as rotas de uma livraria online, a Página Sete. No
fim, /livros/12 abrirá um livro específico, o carrinho terá endereço próprio,
uma área de pedidos exigirá login e uma URL desconhecida mostrará o 404. Os
exemplos foram executados com React 19.2.8, React Router 7.18.2, Vite 8.2.2 e
Caddy 2.10.2 servindo o build.
Por que uma aplicação React precisa de rotas?
Ela precisa de rotas quando mais de uma tela deve ter endereço próprio. Uma
rota é a regra que relaciona um caminho, como /livros, ao componente que
será mostrado; o trecho /livros é o pathname, ou caminho da URL.
Pense na aplicação como uma livraria dentro de um único prédio. As rotas são as placas que indicam catálogo, detalhe do livro, carrinho e pedidos. Trocar de corredor não exige reconstruir o prédio: da mesma forma, uma navegação interna pode trocar o conteúdo sem baixar outro documento HTML. O histórico guarda o caminho percorrido, então voltar funciona, e a placa pode ser fotografada — ou a URL copiada — para outra pessoa chegar ao mesmo lugar.
Essa comparação tem um limite importante: a placa só aponta o destino. O React Router escolhe qual componente combina com a URL, mas não busca o livro no banco, não autentica o cliente e não configura o servidor de produção por você.
Uma SPA (Single Page Application, ou aplicação de página única) normalmente
recebe um index.html e troca partes da interface com JavaScript. O React Router
observa a URL, escolhe a rota correspondente e usa a History API do navegador
para registrar a navegação sem pedir outro documento. Você também pode ler o
caminho diretamente com window.location,
mas teria de reconstruir por conta própria o casamento de rotas, os links e o
histórico que a biblioteca já entrega.
npm create vite@latest livraria-pagina-sete -- --template react
cd livraria-pagina-sete
npm install
npm install react-router@7.18.2
node -p "['react','react-dom','react-router','vite'].map(p => p + ' ' + require(p + '/package.json').version).join('\n')"As quatro linhas confirmam exatamente o ambiente do teste: React e React DOM na
19.2.8, React Router na 7.18.2 e Vite na 8.2.2. O pacote instalado é
react-router, sem -dom no fim. Se você seguiu a lição de
criar projeto React com Vite, a novidade
é somente a última instalação.
A documentação atual oferece três maneiras de organizar o mesmo roteamento:
- no modo Declarative,
<BrowserRouter>envolve a aplicação e<Routes>reúne componentes<Route>; é a entrada mais enxuta para rotas e links; - no modo Data,
createBrowserRoutercria a configuração fora da árvore e<RouterProvider>a fornece à aplicação; ele também permiteloader,actione estados de navegação; - no modo Framework, módulos de rota e o plugin do React Router acrescentam recursos como divisão de código, renderização no servidor e geração estática.
Esta lição usa o modo Data, mas começa apenas com caminhos e componentes. O bloco
congelado importa RouterProvider de react-router, que é válido; em uma
aplicação no navegador, a documentação atual recomenda normalmente importá-lo de
react-router/dom por causa da integração com o React DOM. Se encontrar um
tutorial com <BrowserRouter>, <Routes> e <Route>, não é outra biblioteca:
é o modo Declarative. Escolha um roteador de topo para o projeto e siga o padrão
dele.
Como o createBrowserRouter transforma URLs em telas?
Ele recebe uma árvore de objetos de rota e escolhe o element cujo path
combina com a URL atual. O roteador criado por createBrowserRouter entra na
aplicação por um RouterProvider.
No exemplo mínimo abaixo, cada objeto é como uma linha do mapa da livraria:
path informa o endereço e element informa qual componente ocupa a tela.
// src/main.jsx
import { createRoot } from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router';
import Home from './paginas/Home';
import Livros from './paginas/Livros';
import LivroDetalhe from './paginas/LivroDetalhe';
import Carrinho from './paginas/Carrinho';
import NaoEncontrada from './paginas/NaoEncontrada';
const rotas = [
{ path: '/', element: <Home /> },
{ path: '/livros', element: <Livros /> },
{ path: '/livros/:id', element: <LivroDetalhe /> },
{ path: '/carrinho', element: <Carrinho /> },
{ path: '*', element: <NaoEncontrada /> },
];
const router = createBrowserRouter(rotas);
createRoot(document.getElementById('root')).render(<RouterProvider router={router} />);Dois caminhos não são literais. :id é um segmento dinâmico: ele aceita um
valor naquela posição e o guarda com o nome id. O * é um curinga: ele
combina com o que não foi atendido pelas rotas mais específicas e mostra a tela
de 404.
Para ver o roteador escolhendo, dá para rodar a mesma árvore fora do navegador.
O createMemoryRouter guarda a URL na memória em vez de na barra de endereços,
e aí renderToStaticMarkup mostra o HTML que cada caminho produz:
import { renderToStaticMarkup } from 'react-dom/server';
import { createMemoryRouter, RouterProvider } from 'react-router';
for (const url of ['/', '/livros', '/livros/12', '/livros/999', '/promocoes']) {
const router = createMemoryRouter(rotas, { initialEntries: [url] });
console.log(url.padEnd(13), '->', renderToStaticMarkup(<RouterProvider router={router} />));
}Leia a saída como um mapa sendo percorrido. / mostra o título da livraria;
/livros mostra os três links; /livros/12 mostra Torto Arado. Em
/livros/999, a rota dinâmica combinou e o componente rodou, mas ele não
encontrou o dado. Já /promocoes não combinou com nenhuma rota específica e
caiu no curinga. Casar uma rota e encontrar um registro são problemas diferentes.
A ordem das rotas muda qual tela aparece?
Neste exemplo, não: como as rotas têm especificidades diferentes, o React Router
classifica cada uma antes de escolher. Por isso uma rota literal vence um
segmento dinâmico, e o curinga perde para as duas. O exemplo põe * em primeiro
lugar de propósito:
const rotas = [
{ path: '*', element: <p>404</p> },
{ path: '/livros/:id', element: <p>detalhe do livro</p> },
{ path: '/livros/novidades', element: <p>lançamentos do mês</p> },
];
// e o mesmo laço de createMemoryRouter + renderToStaticMarkup do bloco anteriorAs três linhas comprovam a regra: /livros/12 usa o segmento dinâmico,
/livros/novidades prefere a rota literal e /promocoes sobra para o curinga.
Você pode organizar o array para facilitar a leitura sem depender da posição
para obter esse resultado. A posição ainda pode desempatar rotas irmãs com a
mesma pontuação; evite caminhos equivalentes que disputem a mesma URL.
Experimente você mesmo
Antes de executar, faça a previsão: se a rota * continuar no início e você
trocar /livros/novidades por /livros/promocoes, qual componente vencerá?
No projeto Vite criado no início, substitua apenas o array rotas de
src/main.jsx pelo segundo array, mantendo createBrowserRouter e
RouterProvider. Rode npm run dev e digite /livros/novidades na barra do
navegador: a rota literal deve mostrar “lançamentos do mês”. Teste depois
/livros/qualquer-coisa, que deve mostrar o detalhe, e /promocoes, que deve
mostrar o 404. Assim você executa JSX pelo Vite, sem depender de uma ferramenta
extra para o Node entender JSX.
Até aqui, o mapa já tem endereços estáticos, um parâmetro dinâmico e uma saída para URLs desconhecidas. Agora falta transformar o menu em navegação interna.
Por que usar Link e NavLink nos caminhos internos?
Porque eles fazem a navegação pelo roteador e preservam a aplicação já carregada.
Num clique comum em <a href="/carrinho">, o navegador pede outro documento,
descarta o estado que só existia na memória, baixa os arquivos novamente e monta
o React do zero.
O Link continua produzindo um link acessível no HTML, mas assume os cliques que
podem ser tratados no cliente: atualiza o histórico e avisa o roteador sem pedir
outro documento. Uma âncora <a> continua sendo a escolha certa para um site
externo, um arquivo ou uma navegação que deve recarregar de propósito.
// src/paginas/Livros.jsx
import { Link } from 'react-router';
import { catalogo } from '../dados';
export default function Livros() {
return (
<ul>
{catalogo.map((livro) => (
<li key={livro.id}>
<Link to={`/livros/${livro.id}`}>{livro.titulo}</Link>
</li>
))}
</ul>
);
}A key aí não tem nada a ver com rota — é a regra de
renderizar listas no React, que continua
valendo dentro do menu.
Para provar a diferença, montei a aplicação no jsdom (um navegador de mentira que roda no Node), contei quantas vezes o componente de casca montou e cliquei nos dois tipos de link:
let montagens = 0;
function Casca() {
useEffect(() => {
montagens += 1;
console.log('Casca montou — total de montagens:', montagens);
}, []);
return (
<div>
<Link id="spa" to="/livros/12">Torto Arado (Link)</Link>
<a id="cru" href="/carrinho">Carrinho (a)</a>
<main><Outlet /></main>
</div>
);
}Nas linhas abaixo, “na tela” é o textContent do <main>: somente o conteúdo
da rota filha, sem os dois links da casca.
Leia os quatro fatos da saída. A casca montou uma vez; o Link mudou a URL para
/livros/12; o conteúdo virou o detalhe de Torto Arado; e a contagem continuou
em 1. No clique da âncora, o jsdom avisou navigation to another Document porque
não implementa a carga de outro documento. Num navegador real, um clique interno
normal nessa âncora inicia justamente essa nova carga.
Quando o NavLink deve usar end?
Use end quando o item deve ficar ativo somente na URL exata. O NavLink é um
Link que também informa se o destino está ativo. Ele aplica a classe active
e aria-current="page", informação importante para tecnologias assistivas. Sem
end, uma rota descendente também mantém o item da seção ativo:
<NavLink to="/">Início</NavLink>
<NavLink to="/livros">Catálogo</NavLink>
<NavLink to="/livros" end>Catálogo (end)</NavLink>A saída mostra três regras. Em /, apenas “Início” fica ativo. Em /livros, os
dois links de catálogo ficam ativos. Em /livros/12, o catálogo sem end
continua aceso por representar a seção, enquanto o que tem end apaga. O
próprio React Router trata to="/" como exceção: ele só fica ativo na raiz.
Experimente você mesmo
Antes de abrir o navegador, preveja em qual endereço “Catálogo” ficará ativo.
Ainda em src/main.jsx, adicione NavLink ao import do React Router, envolva as
três linhas do exemplo num componente Menu. Para isolar o teste, substitua
temporariamente todo o array de rotas por uma única rota:
[{ path: '*', element: <Menu /> }]. Rode npm run dev e visite primeiro
/livros e depois /livros/12. O link com end deve ter active e
aria-current="page" apenas na primeira URL. Remova end e repita: o menu de
seção deve ficar ativo nas duas telas. Na próxima seção você restaurará as rotas
e transformará esse menu temporário no Layout definitivo.
O que significa o erro que cita NavigationContext?
Ele significa que um componente de roteamento foi renderizado fora do roteador.
Um Link sem o contexto fornecido pelo roteador quebra assim:
function Rodape() {
return <Link to="/livros">Ver o catálogo</Link>;
}
console.log(renderToStaticMarkup(<Rodape />));TypeError: Cannot destructure property ‘basename’ of ‘React10.useContext(…)’ as it is null. at LinkWithRef (file:///private/tmp/livraria-pagina-sete/node_modules/react-router/dist/development/chunk-62JRHF6Z.mjs:10518:11)
A mensagem cita basename e NavigationContext, palavras que você não escreveu,
porque o erro acontece dentro da biblioteca. O diagnóstico é: Link, NavLink,
useNavigate ou useLocation tentaram ler o contexto do roteador e receberam
null. useParams é diferente: fora de uma rota correspondente, ele pode
devolver um objeto vazio em vez deste erro. Na aplicação, corrija mantendo o
componente abaixo do RouterProvider; em teste, monte-o com um
createMemoryRouter e seu provider. Componentes isolados no Storybook e uma
segunda raiz criada fora do provider são outras causas comuns.
Resumo desta etapa: Link navega sem remontar a aplicação, NavLink acrescenta
o estado ativo e todos eles precisam viver dentro de um roteador.
Como ler um parâmetro da URL com useParams?
Chame useParams dentro do componente da rota. Ele devolve um objeto em que a
chave é o nome escrito depois de : no path e o valor é o texto recebido na
URL.
// src/paginas/LivroDetalhe.jsx
import { useParams } from 'react-router';
import { buscarLivro } from '../dados';
export default function LivroDetalhe() {
const { id } = useParams();
const livro = buscarLivro(id);
if (!livro) return <p>Livro não encontrado no catálogo.</p>;
return (
<article>
<h2>{livro.titulo}</h2>
<p>{livro.autor}</p>
<p>R$ {livro.preco.toFixed(2)}</p>
</article>
);
}O detalhe que costuma causar o defeito está no tipo. Um parâmetro presente chega
como string, porque nasceu de um trecho da URL; se um segmento opcional não
aparecer, seu valor pode ser undefined. O roteador não tem como adivinhar que
12 representa um número. O componente espião abaixo mostra o valor em duas
rotas:
function Espiao() {
const params = useParams();
console.log('params:', JSON.stringify(params), '| typeof id:', typeof params.id);
console.log(' buscando com id :', catalogo.find((l) => l.id === params.id)?.titulo);
console.log(' buscando com Number(id):', catalogo.find((l) => l.id === Number(params.id))?.titulo);
return null;
}As duas primeiras linhas de cada URL confirmam que o objeto contém texto:
"12" e "41", ambos do tipo string. Por isso l.id === params.id compara
número com string e não encontra nada. Depois de Number(id), a mesma busca
encontra Torto Arado e Vidas Secas. O diagnóstico é incompatibilidade de tipos;
a correção é converter e validar o parâmetro na entrada, antes de usá-lo no
restante da aplicação.
Como reutilizar o layout com rotas aninhadas e Outlet?
Coloque as rotas filhas em children e marque com <Outlet /> o ponto do layout
onde a filha selecionada deve aparecer. Assim, cabeçalho, menu e rodapé ficam no
elemento pai e não precisam ser repetidos em cada tela.
Voltando ao mapa da livraria, o layout é o salão fixo e o Outlet é o espaço de
exposição que recebe o conteúdo do corredor atual. A ideia se aproxima da
composição com children, mas quem
escolhe o conteúdo aqui é a rota que combinou com a URL, não uma prop enviada
manualmente.
// src/Layout.jsx
import { NavLink, Outlet } from 'react-router';
export default function Layout() {
return (
<div>
<header>
<NavLink to="/">Início</NavLink>
<NavLink to="/livros">Catálogo</NavLink>
<NavLink to="/carrinho">Carrinho</NavLink>
</header>
<main>
<Outlet />
</main>
<footer>Livraria Página Sete — desde 2019</footer>
</div>
);
}// src/rotas.jsx
export const rotas = [
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'livros', element: <Livros /> },
{ path: 'livros/:id', element: <LivroDetalhe /> },
{ path: 'carrinho', element: <Carrinho /> },
{ path: '*', element: <NaoEncontrada /> },
],
},
];Três detalhes trabalham juntos. Os caminhos dos filhos perderam a barra inicial
porque são relativos ao pai: livros dentro de path: '/' forma /livros.
index: true identifica a filha padrão, mostrada quando a URL é exatamente a do
pai. E o * está entre os filhos, por isso até a tela de 404 aparece dentro do
mesmo cabeçalho e rodapé.
Você até pode colocar dois <Outlet /> no mesmo layout, mas ambos renderizam a
mesma filha que combinou com a URL. Eles não funcionam como espaços nomeados e
independentes. Para controlar duas áreas diferentes, aninhe outro nível de rota
ou componha uma das áreas com props.
Renderizando /livros/12 com essa árvore:
Na saída, o <Outlet /> não aparece como tag: em seu lugar está o <article> de
Torto Arado. O cabeçalho e o rodapé continuam ao redor, enquanto
aria-current="page" marca “Catálogo” como ativo. data-discover é um atributo
interno do React Router e não exige nenhuma ação sua.
Resumo do encaixe: o pai preserva a estrutura, a rota filha escolhe o conteúdo e
o Outlet indica exatamente onde os dois se encontram.
Como navegar por código depois de enviar um formulário?
Obtenha a função de navegação com useNavigate e chame-a no evento que conclui a
ação. É o caminho certo quando a mudança de tela depende de algo que seu código
acabou de fazer, e não de um link escolhido antecipadamente.
function LivroDetalhe() {
const { id } = useParams();
const navegar = useNavigate();
function aoEnviar(evento) {
evento.preventDefault();
const quantidade = Number(new FormData(evento.target).get('quantidade'));
navegar('/carrinho', { state: { id: Number(id), quantidade } });
}
return (
<form onSubmit={aoEnviar}>
<input name="quantidade" type="number" defaultValue={2} />
<button type="submit">Adicionar ao carrinho</button>
</form>
);
}O segundo argumento aceita state, um dado salvo no histórico da navegação sem
aparecer na URL. A tela de destino o lê com useLocation. No teste, o formulário
foi enviado no jsdom:
A primeira linha mostra a rota do livro; a segunda confirma três mudanças: a URL
virou /carrinho, a tela de destino foi renderizada e o objeto
{"id":12,"quantidade":2} chegou inteiro pelo state. O preventDefault é
necessário nesse exemplo: sem ele, o navegador envia o formulário como uma
navegação de documento antes que o fluxo do cliente termine. Se houver campos
controlados, revise formulário controlado no React.
Como esta lição usa o modo Data, prefira o componente <Form> do React Router,
uma função action e redirect quando o próprio roteador gerenciar o envio.
Nesse fluxo, o Form captura o envio, a action processa os dados e o
redirect escolhe a próxima URL; o roteador também organiza estados de
navegação e erros da rota. useNavigate continua apropriado para um evento
imperativo da interface, como fechar um modal e seguir para outra tela; o
formulário local acima o mantém visível para você entender a função isoladamente.
O aviso pode ser reproduzido chamando navegar('/carrinho') durante a
renderização:
A saída informa a correção literalmente: chame em um evento ou em um efeito.
Neste teste, a URL permaneceu em /livros/12 durante a primeira renderização.
Isso não transforma o corpo do componente em um lugar válido para tentar de
novo: navegar é um efeito colateral; renderizar deve apenas descrever a
interface.
Como exigir login e voltar à rota que a pessoa tentou abrir?
Guarde a localização desejada no state, envie quem não tem sessão para o login
e, depois da entrada, navegue de volta usando replace. O componente abaixo faz
a primeira decisão: mostra o conteúdo para quem está autenticado ou renderiza um
Navigate para quem precisa entrar.
function RotaPrivada({ children }) {
const local = useLocation();
if (!clienteLogado) {
return <Navigate to="/entrar" state={{ de: local.pathname }} replace />;
}
return children;
}const rotas = [
{ path: '/livros/:id', element: <Livro /> },
{ path: '/entrar', element: <Entrar /> },
{
path: '/conta/pedidos',
element: (
<RotaPrivada>
<Pedidos />
</RotaPrivada>
),
},
];<Navigate> é a alternativa declarativa à função de useNavigate: ao ser
renderizado, ele muda a localização. O state leva o pathname que a pessoa
tentou abrir para a tela de login saber qual é o destino de volta:
function Entrar() {
const { state } = useLocation();
const navegar = useNavigate();
const destino = state?.de ?? '/';
return (
<button
onClick={() => {
clienteLogado = 'ana@paginasete.com.br';
navegar(destino, { replace: true });
}}
>
Entrar
</button>
);
}O teste percorre quatro passos: a pessoa está num livro, tenta abrir “Meus pedidos”, entra e depois usa o botão voltar do navegador.
Com replace: true, a quarta linha volta diretamente para /livros/12. A rota
de login não ficou presa no histórico. Compare com o mesmo percurso usando uma
navegação normal:
Sem replace, a linha 4 volta para /entrar, e o log mostra que essa tela recebeu
de novo o destino /conta/pedidos. O histórico ficou com login e pedidos
empilhados, criando o conhecido vai e volta entre as duas telas. O diagnóstico
está na sequência das URLs; a correção é substituir a entrada de login em vez de
adicionar outra.
Por que uma rota funciona no Vite e dá 404 depois do deploy?
Porque o servidor de produção pode procurar um arquivo chamado /livros/12 em
vez de entregar o index.html para o React Router. No npm run dev, o Vite já
faz esse fallback; uma hospedagem precisa ser configurada para fazer o mesmo.
Ao clicar num Link, a biblioteca trata a navegação no cliente. Ao recarregar
/livros/12 ou abrir essa URL diretamente, porém, o primeiro pedido vai ao
servidor. Como não existe dist/livros/12, ele devolve 404 antes que o JavaScript
e o roteador tenham a chance de executar.
O diagrama separa os dois fluxos. Em cima, o servidor procura um arquivo que não
existe e encerra com 404. Embaixo, try_files devolve o index.html, o
JavaScript carrega e só então o React Router escolhe o livro. O teste começa com
o build e um Caddy configurado apenas para servir arquivos:
npm run build✓ built in 190ms
A saída confirma que o build terminou e criou dist/index.html, além dos
arquivos reais de CSS e JavaScript. O primeiro teste serve exatamente essa pasta,
mas ainda sem a regra de fallback. Salve o próximo bloco num arquivo chamado
Caddyfile.errado, na raiz do projeto:
:4173 {
root * dist
file_server
}caddy run --config Caddyfile.errado --adapter caddyfile
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4173/
curl -si http://localhost:4173/livros/12 | head -12Não cole as três linhas de uma vez. O caddy run fica executando para manter o
servidor no ar: rode somente a primeira linha em um terminal, deixe-o aberto e
execute os dois curl em um segundo terminal, dentro da mesma pasta.
Leia os dois resultados: / responde 200, mas /livros/12 responde 404 Not Found com corpo vazio. Não é um erro do componente nem do array de rotas; o
React Router não executou porque nenhum HTML chegou ao navegador. A correção é
orientar o servidor a entregar index.html quando o caminho não corresponder a
um arquivo real.
:4173 {
root * dist
try_files {path} /index.html
file_server
}Salve essa versão como Caddyfile. No primeiro terminal, pressione Ctrl+C
para parar a configuração errada e inicie a nova com
caddy run --config Caddyfile --adapter caddyfile. Só então execute, no segundo
terminal, os dois comandos abaixo:
curl -si http://localhost:4173/livros/12 | head -9
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://localhost:4173/assets/index-DHNCI_kA.jsAgora /livros/12 responde 200 com os 470 bytes do index.html, enquanto o
bundle continua respondendo 200 com text/javascript. Isso confirma que
try_files preserva os arquivos reais e usa o HTML apenas como fallback. Depois
de receber esse HTML, o navegador carrega o bundle e o React Router finalmente
mostra o livro.
E o servidor de desenvolvimento, que nunca reclamou? Ele já fazia o mesmo fallback desde sempre:
npm run dev -- --port 5173
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://localhost:5173/livros/12Aqui também são dois terminais: rode npm run dev -- --port 5173 no primeiro e
deixe o processo aberto; rode apenas o curl no segundo. Quando terminar,
pressione Ctrl+C no primeiro terminal.
O 200 text/html comprova que o servidor de desenvolvimento já faz o fallback.
Por isso o defeito só aparece no deploy quando a hospedagem não repete essa
configuração.
O nome da diretiva muda de servidor para servidor, mas a ideia é sempre a mesma. Só a linha do Caddy foi medida aqui; as outras são a forma equivalente na documentação de cada um:
| onde | arquivo | a linha |
|---|---|---|
| Caddy | Caddyfile |
try_files {path} /index.html |
| Nginx | bloco location / |
try_files $uri $uri/ /index.html; |
| Apache | .htaccess |
FallbackResource /index.html |
| Netlify | public/_redirects |
/* /index.html 200 |
O que você já consegue construir e qual é o próximo passo?
Você já consegue transformar URLs em telas, navegar com Link e NavLink, ler
parâmetros, encaixar rotas filhas em um Outlet, redirecionar após ações,
preservar o destino de login e corrigir o fallback de produção.
O mapa da livraria agora funciona do navegador ao servidor: cada corredor tem endereço, o histórico registra o caminho e uma URL desconhecida recebe uma tela própria. O limite continua valendo — o roteador organiza a navegação, mas estilo, dados e segurança pertencem a outras partes da aplicação.
A próxima lição é CSS no React. Ela parte do mesmo
layout com cabeçalho, main, rodapé e Outlet para mostrar como estilizar cada
tela sem deixar as regras vazarem para as demais.
Perguntas frequentes
React Router vem junto com o React?
Qual a diferença entre createBrowserRouter e createHashRouter?
Dá para ter mais de um Outlet no mesmo layout?
Preciso do React Router para uma página só?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0 · React 19.2.8 · React Router 7.18.2 · Vite 8.2.2 · Caddy 2.10.2, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- React Router — Changelog — reactrouter.com
- React Router — Picking a Mode — reactrouter.com
- React Router — useNavigate — reactrouter.com
- MDN — History API — developer.mozilla.org
- Caddy — try_files — caddyserver.com
- Google Search — JavaScript SEO e soft 404 — developers.google.com


