- Artigos
- Desenvolvimento
- Entendendo Rotas Dinâmicas no Nuxt 4

Todo produto acaba precisando de URLs que mudam conforme os dados: um perfil em /users/42, um post em /blog/meu-primeiro-artigo ou uma seção de documentação em /docs/primeiros-passos/instalacao. No Nuxt 4, você não monta esses caminhos manualmente em um arquivo central de rotas. Você expressa a estrutura com pastas e arquivos, e o Nuxt gera as rotas automaticamente.
Esse modelo mantém o roteamento perto dos componentes que renderizam cada página e escala bem conforme o app cresce. Quando você entende as convenções de nomenclatura e como os params chegam até a página, rotas dinâmicas viram uma das partes mais previsíveis de um projeto Nuxt.
Como funcionam as rotas dinâmicas baseadas em arquivos
O Nuxt 4 usa Vue Router por baixo dos panos, mas a maioria dos apps nunca mexe em um router.ts. Cada arquivo dentro de app/pages/ (ou pages/, se você mantiver a estrutura clássica) vira uma rota. Coloque colchetes no nome do segmento para marcá-lo como dinâmico.
app/pages/users/[id].vue atende /users/1, /users/abc e qualquer outro segmento único.
app/pages/blog/[slug].vue atende /blog/hello-world.
app/pages/docs/[...slug].vue é uma rota catch-all que atende /docs/a, /docs/a/b e caminhos mais profundos.
app/pages/archive/[[year]].vue usa colchetes duplos para param opcional, então /archive e /archive/2024 caem na mesma página.
O nome dentro dos colchetes vira a chave do param. Um arquivo [id].vue expõe route.params.id. Um catch-all [...slug].vue entrega route.params.slug como array de segmentos — ideal para árvores de documentação ou paths vindos de CMS.
Lendo params em pages e layouts
Dentro de qualquer page, use o composable useRoute() para ler o estado atual da URL:
const route = useRoute()
const userId = route.params.idOs params são reativos. Quando o usuário navega de /users/1 para /users/2 sem recarregar a página, o mesmo componente é reutilizado e route.params é atualizado. Por isso, não assuma que uma leitura única no setup basta para carregar dados — observe o param ou amarre sua lógica assíncrona a ele.
Um padrão confiável é derivar uma chave de fetch do param e passá-la para useAsyncData ou useFetch:
const route = useRoute()
const { data: user, pending, error } = await useAsyncData(
() => `user-${route.params.id}`,
() => $fetch(`/api/users/${route.params.id}`)
)Quando route.params.id muda, o Nuxt refaz o fetch porque a chave mudou. Isso evita UI desatualizada quando o usuário alterna entre registros em um layout lista/detalhe.
Rotas tipadas no Nuxt 4
Uma melhoria prática do Nuxt moderno é a integração mais forte com TypeScript no roteamento. Com rotas tipadas habilitadas, os nomes de params inferidos da estrutura de arquivos aparecem no autocomplete, e rotas inválidas quebram em compile time em vez de só em runtime.
Isso faz diferença em times onde URLs fazem parte da API pública. Se você renomear [slug].vue para [postSlug].vue, o TypeScript aponta cada navigateTo e cada useRoute().params que ainda esperam slug. Trate params de rota como qualquer outra fronteira tipada do app.
Rotas dinâmicas aninhadas
Apps reais combinam segmentos estáticos e dinâmicos no mesmo path. Por exemplo:
app/pages/orgs/[orgId]/projects/[projectId].vue → /orgs/acme/projects/website
app/pages/shop/[category]/[product].vue → /shop/shoes/running-pro
Cada nível de pasta pode ser estático ou dinâmico. Os params se acumulam, então a page acima expõe orgId e projectId. Rotas aninhadas brilham quando cada segmento representa um nível de recurso: organização, time, projeto, tarefa.
Mantenha o aninhamento raso quando possível. URLs profundas são difíceis de ler e dolorosas de refatorar. Se precisar de mais de três níveis dinâmicos, considere um catch-all com uma função que resolve slugs para entidades na API.
Navegando para rotas dinâmicas
Use NuxtLink para navegação declarativa:
<NuxtLink :to="`/users/${user.id}`">Ver perfil</NuxtLink>Para navegação programática, prefira navigateTo em vez de chamar o Vue Router direto, para manter middleware e comportamento de servidor consistentes:
await navigateTo(`/blog/${post.slug}`)Quando você conhece os valores do param em compile time, a sintaxe com objeto fica mais clara e funciona melhor com rotas tipadas:
await navigateTo({
name: 'blog-slug',
params: { slug: post.slug }
})Os nomes de rota seguem convenções do Nuxt baseadas no caminho do arquivo. Consulte a lista gerada no DevTools ou na IDE quando tiver dúvida.
Validando params antes de renderizar
Segmentos dinâmicos aceitam qualquer string que o usuário digitar — lixo, tentativas de injection, UUIDs inexistentes no banco. Não use só a página de erro da API como única proteção.
O Nuxt permite validar params com definePageMeta e uma função validate:
definePageMeta({
validate: async (route) => {
const id = route.params.id
return typeof id === 'string' && /^\d+$/.test(id)
}
})Se a validação retornar false, o Nuxt renderiza o error.vue mais próximo com 404. Você também pode buscar metadados leves dentro de validate para confirmar que um slug existe antes de queries mais pesadas na page. Mantenha a validação rápida; ela roda a cada navegação para aquela rota.
Rotas dinâmicas e fetch de dados
SSR adiciona uma restrição: os params precisam estar disponíveis quando a page executa no servidor. Por isso await useAsyncData no top level do setup é idiomático no Nuxt. O param é lido da URL da requisição durante o SSR, os dados são buscados e o HTML chega já preenchido.
Fique atento a estes erros comuns:
Fetch só no onMounted — funciona na navegação client-side, mas prejudica SEO e first paint em visitas diretas.
Ignorar o tipo do param — catch-all devolve array; segmento único devolve string. Normalize cedo.
Hardcode de paths de API — centralize builders de URL para renomear params sem quebrar fetches em silêncio.
Pular empty states — quando um param válido não encontra registro, mostre um not-found claro em vez de página em branco.
Catch-all para CMS e conteúdo markdown
Sites orientados a conteúdo muitas vezes guardam paths no CMS em vez de um arquivo Vue por página. Uma única page catch-all carrega o path, pede o documento ao CMS e renderiza um componente dinâmico ou corpo rich text.
const route = useRoute()
const path = Array.isArray(route.params.slug)
? route.params.slug.join('/')
: route.params.slug
const { data: doc } = await useAsyncData(
() => `doc-${path}`,
() => queryCollection('content').path(`/${path}`).first()
)Combine isso com middleware que normaliza trailing slash e lowercase para /About e /about/ não duplicarem conteúdo nem dividirem analytics.
SEO, URLs canônicas e meta tags
Pages dinâmicas precisam de metadata explícita. Use useSeoMeta ou useHead com valores do registro carregado:
useSeoMeta({
title: () => post.value?.title,
description: () => post.value?.excerpt,
ogImage: () => post.value?.coverImage
})Defina URL canônica quando o mesmo conteúdo puder aparecer em paths diferentes. Em variantes paginadas ou filtradas do mesmo recurso, canonical evita indexação duplicada.
Testando e depurando rotas dinâmicas
Em desenvolvimento, abra o Nuxt DevTools e inspecione a aba de router para ver cada rota gerada, seus params e os componentes correspondentes. Quando uma page retorna 404 inesperado, o problema costuma ser typo no nome do arquivo ([id].vue vs [userId].vue), falta de um index irmão ou a função validate rejeitando o param.
Em testes end-to-end, navegue direto para URLs dinâmicas, não apenas clicando em listas. Visitas diretas exercitam o tratamento de params no SSR, onde bugs sutis aparecem.
Amarrando tudo
Rotas dinâmicas no Nuxt 4 são convenção em vez de configuração. Nomeie arquivos com segmentos entre colchetes, leia params via useRoute(), amarre dados assíncronos às mudanças de param, valide entrada não confiável cedo e gere links com NuxtLink ou navigateTo. Seja em CRUD administrativo, sites de marketing ou dashboards SaaS multi-tenant, os mesmos padrões se repetem: um arquivo por forma de rota, params como ponte entre URL e dados, e navegação tipada e validada mantendo a experiência consistente para o usuário e sustentável para o time.