Capítulo 311 de 456

App Router (Migration Guide)

Core Idea

Guia de migração incremental do Pages Router pro App Router (Next.js 13+): os dois diretórios (pages e app) coexistem, permitindo migrar página por página em vez de tudo de uma vez.

Key Concepts

  • Coexistência: app e pages funcionam simultaneamente; features novas (<Image>, <Link>, <Script>, next/font) funcionam em ambos sem precisar migrar tudo.
  • <Link> sem <a> filho: virou padrão a partir da v13 (antes era experimental na v12.2); agora <Link href="/about">About</Link> renderiza <a> sozinho.
  • Root layout (app/layout.tsx): substitui pages/_app.tsx + pages/_document.tsx; precisa definir <html> e <body> manualmente, e é obrigatório em todo projeto com app.
  • getLayout() pattern → nested layouts: o padrão antigo de Page.getLayout vira layout.js nativo em app, com a lógica de UI compartilhada movida pra um Client Component se precisar de interatividade.
  • next/head → Metadata API: <Head><title>...</title></Head> vira export const metadata = { title: ... } no arquivo de page/layout.
  • Roteamento por pasta + page.js: pages/about.jsapp/about/page.js; pages/blog/[slug].jsapp/blog/[slug]/page.js.
  • Novos hooks (next/navigation): useRouter, usePathname, useSearchParams substituem o useRouter de next/router; só funcionam em Client Components. useRouter novo não retorna pathname, query, isFallback, locale, asPath, isReady nem route.
  • next/compat/router: useRouter de compatibilidade pra componente compartilhado entre pages e app durante a migração.
  • Data fetching: getServerSideProps/getStaticProps/getInitialProps viram fetch() dentro de Server Components async, com cache: 'no-store' (equivalente a SSR), cache: 'force-cache' (equivalente a SSG, default), e next: { revalidate: N } (equivalente a ISR).
  • getStaticPathsgenerateStaticParams: retorna array de objetos de segmento ([{ id: '1' }, { id: '2' }]) em vez de { params: {...} }; pode ser usado em layouts.
  • fallbackdynamicParams: config.dynamicParams (default true) substitui fallback: true/false/'blocking'; true gera sob demanda e cacheia, false retorna 404 pra params fora de generateStaticParams.
  • req/cookies/headers: getServerSideProps({ req }) vira as funções headers()/cookies() de next/headers, baseadas nas Web APIs.
  • API Routes → Route Handlers: pages/api/* continua funcionando; em app, o equivalente é app/api/route.ts exportando GET/POST/etc usando Request/Response nativos da Web.
  • Global styles sem restrição: em pages, CSS global só podia entrar em _app.js; em app, qualquer layout/page/componente pode importar CSS global.

Code Examples

async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  return res.json()
}

export default async function Dashboard() {
  const projects = await getProjects()
  return <ul>{projects.map((p) => <li key={p.id}>{p.name}</li>)}</ul>
}
  • O que demonstra: substituição direta de getServerSideProps por fetch com cache: 'no-store' dentro de um Server Component async.
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}

async function getPost(params) {
  const res = await fetch(`https://.../posts/${(await params).id}`)
  return res.json()
}

export default async function Post({ params }) {
  const post = await getPost(params)
  return <PostLayout post={post} />
}
  • O que demonstra: generateStaticParams no lugar de getStaticPaths, com fetch de dados direto no componente.

Reference Tables

pages Directoryapp Directory
index.jspage.js
about.jsabout/page.js
blog/[slug].jsblog/[slug]/page.js
getServerSidePropsfetch(url, { cache: 'no-store' })
getStaticPropsfetch(url) (default force-cache)
getStaticProps + revalidatefetch(url, { next: { revalidate: N } })
getStaticPathsgenerateStaticParams
fallback: true/false/'blocking'dynamicParams = true/false
pages/_app.js + pages/_document.jsapp/layout.js (root layout)
pages/_error.jserror.js
pages/404.jsnot-found.js
pages/api/*app/**/route.js

Anti-patterns

  • Deletar _app/_document cedo demais: manter durante a migração incremental pra não quebrar rotas pages/* ainda não migradas; só apagar após migração completa.
  • Usar useRouter de next/router em Client Component dentro de app: não é suportado; precisa ser next/navigation.
  • Esperar prefetch cruzado entre routers: navegação entre rota servida por app e por pages é hard navigation, sem prefetch automático.

Key Takeaways

  1. Migração recomendada é incremental: página por página, mantendo pages e app coexistindo.
  2. Node.js mínimo v18.17 e Next.js 13.4+ são pré-requisitos pra usar app.
  3. Toda API de data fetching antiga tem equivalente direto baseado em fetch() com opções de cache.
  4. Root layout é obrigatório e substitui _app+_document numa peça só.

Connects To

  • ch312 Create React App: outro guia de migração que também usa app directory como destino.
  • ch305 ISR: next: { revalidate } no App Router é o equivalente direto do revalidate do Pages Router.