Capítulo 113 de 456

Dynamic Segments

Core Idea

Dynamic Segments capturam um valor da URL para gerar rotas a partir de dado dinâmico, quando o valor do segmento não é conhecido de antemão. Criados envolvendo o nome da pasta em colchetes ([folderName]); o valor capturado chega via prop params (Promise) em layout, page, route e generateMetadata.

Key Concepts

  • [folderName]: segmento dinâmico simples, captura um valor ({ slug: 'a' }).
  • [...folderName]: catch-all, captura múltiplos segmentos como array ({ slug: ['a','b'] }); não casa com a rota base sem sufixo.
  • [[...folderName]]: optional catch-all, também casa com a rota sem sufixo ({ slug: undefined }).
  • Root parameters: dynamic segments que aparecem antes do root layout; podem ser lidos de qualquer Server Component via next/root-params.
  • params é Promise: requer await ou use() (React) para acessar; em Client Components use use(params) ou o hook useParams().
  • Tipagem TS: use PageProps<'/route'>, LayoutProps<'/route'> ou RouteContext<'/route'> para tipar params; valores são sempre string, string[] ou undefined.
  • Com Cache Components: sem generateStaticParams, params são dado runtime e precisam de <Suspense>; com generateStaticParams, o build valida e prerenderiza as amostras, e branches condicionais não cobertos pelas amostras só são validados na primeira request real.

Code Examples

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <div>My Post: {slug}</div>
}
  • O que demonstra: leitura básica de um dynamic segment via await params.
import { notFound } from 'next/navigation'
import type { Locale } from '@i18n/types'
import { isValidLocale } from '@i18n/utils'

function assertValidLocale(value: string): asserts value is Locale {
  if (!isValidLocale(value)) notFound()
}

export default async function Page(props: PageProps<'/[locale]'>) {
  const { locale } = await props.params // locale is typed as string
  assertValidLocale(locale)
  // locale is now typed as Locale
}
  • O que demonstra: validação runtime de um param com conjunto de valores conhecido, estreitando o tipo de string para Locale via type guard + notFound().
export async function generateStaticParams() {
  return [{ slug: '1' }, { slug: '2' }, { slug: '3' }]
}

export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
  const { slug } = await params
  return <Content slug={slug} />
}
  • O que demonstra: prerender de rotas dinâmicas usando generateStaticParams com amostras conhecidas em build time.

Reference Tables

RouteExample URLparams
app/blog/[slug]/page.js/blog/a{ slug: 'a' }
app/shop/[...slug]/page.js/shop/a/b{ slug: ['a', 'b'] }
app/shop/[[...slug]]/page.js/shop{ slug: undefined }
app/[categoryId]/[itemId]/page.js-{ categoryId: string, itemId: string }

Anti-patterns

  • Acessar params de forma síncrona: ainda funciona por compat (v15), mas está deprecated; sempre await ou use().
  • await params no topo de um layout: impede o layout de ser prerenderizado; passe a Promise adiante e aguarde só no componente que precisa do valor.
  • Confiar que generateStaticParams cobre todo branch condicional: branches de código que só disparam para params fora da amostra não são validados no build; se acessam API runtime sem <Suspense>, falham só na primeira request real.
  • Assumir params como tipo estreito sem validação runtime: valores vêm sempre como string/string[]/undefined, qualquer restrição (ex. enum de locale) precisa de validação explícita + notFound().

Key Takeaways

  1. Catch-all ([...x]) não casa com a rota sem sufixo; optional catch-all ([[...x]]) casa.
  2. fetch dentro de generateStaticParams é deduplicado automaticamente entre chamadas.
  3. generateStaticParams funciona também em Route Handlers dinâmicos (route.ts), gerando respostas de API estáticas no build.
  4. Sem <Suspense> em torno de dado runtime (ex. cookies()) dentro de um branch condicional de params não amostrado, a primeira request real falha.

Connects To

  • File-system conventions (ch111): índice das convenções.
  • default.js (ch112): também recebe params como Promise pela mesma mecânica.
  • layout.js (ch119): aviso específico sobre não aguardar params no topo do layout.