Capítulo 57 de 456

App Router

Core Idea

Guide for upgrading from Next.js 12 to 13+ and incrementally migrating an existing pages directory application to the app directory (App Router), page by page, without a hard cutover.

Key Concepts

  • Incremental migration: app and pages directories coexist; you migrate route by route while keeping the rest on pages.
  • Root layout (app/layout.tsx): Required file that replaces pages/_app.tsx + pages/_document.tsx; must define <html> and <body> since Next.js does not inject them automatically.
  • Server Components by default: Pages inside app are Server Components unless marked 'use client', unlike pages where all page components are Client Components.
  • generateStaticParams: Replaces getStaticPaths; returns an array of param objects (segments) instead of nested { params } objects or path strings.
  • dynamicParams config: Replaces fallback: true | false | 'blocking' from getStaticPathstrue (default) generates unknown params on demand, false 404s them.
  • headers() / cookies(): Read-only functions from next/headers, used in Server Components, replacing req.headers/req.cookies from getServerSideProps.
  • Route Handlers (route.ts): Replace pages/api/*, built on Web Request/Response APIs.
  • New navigation hooks: useRouter, usePathname, useSearchParams from next/navigation replace the next/router hook; only work in Client Components.
  • next/compat/router: Compatibility useRouter hook for sharing components between pages and app during migration.

Code Examples

// app/layout.tsx — required root layout
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
  • O que demonstra: estrutura mínima obrigatória de um root layout no App Router.
// app/dashboard/page.tsx — data fetching replaces getServerSideProps
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((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}
  • O que demonstra: cache: 'no-store' reproduz o comportamento de getServerSideProps (busca a cada request).
// app/posts/[id]/page.tsx — generateStaticParams replaces getStaticPaths
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}

export default async function Post({ params }: { params: { id: string } }) {
  const post = await getPost(params)
  return <PostLayout post={post} />
}
  • O que demonstra: generateStaticParams retorna array plano de segmentos, mais simples que getStaticPaths.

Reference Tables

pages Directoryapp DirectoryRoute
index.jspage.js/
about.jsabout/page.js/about
blog/[slug].jsblog/[slug]/page.js/blog/post-1
pages APIapp equivalent
getServerSidePropsfetch(url, { cache: 'no-store' }) em Server Component
getStaticPropsfetch(url) (default force-cache)
getStaticProps com revalidatefetch(url, { next: { revalidate: N } })
getStaticPathsgenerateStaticParams
getStaticPaths fallbackdynamicParams (route segment config)
pages/_app.js + pages/_document.jsapp/layout.js (root layout)
pages/_error.jserror.js
pages/404.jsnot-found.js
pages/api/*route.js (Route Handlers)
next/headMetadata API (export const metadata)
useRouter de next/routeruseRouter/usePathname/useSearchParams de next/navigation

Anti-patterns

  • Migrar _app/_document apagando os originais antes de terminar: quebra as rotas restantes em pages/*; manter ambos até a migração completa.
  • Usar next/head dentro de app: não funciona; usar a Metadata API (export const metadata).
  • Esperar que <Link> cruze routers automaticamente: navegação entre App Router e Pages Router é sempre hard navigation.

Key Takeaways

  1. Node.js mínimo v18.17 e Next.js 13.4+ são pré-requisitos para criar o diretório app.
  2. A rota mais fácil de migrar é mover o componente de página para um Client Component separado e importá-lo num novo page.tsx Server Component.
  3. Data fetching muda de funções especiais (getServerSideProps/getStaticProps) para fetch() com opções de cache/next.revalidate direto no componente async.
  4. Estilos globais deixam de ser restritos a _app.js; podem ser importados em qualquer layout/page/componente do app.
  5. Codemods (next-image-to-legacy-image, new-link, etc.) automatizam boa parte do trabalho mecânico de upgrade.

Connects To

  • Migrating to Cache Components (ch060): próximo passo de migração depois de já estar no App Router, adotando use cache no lugar dos route segment configs.
  • Create React App / Vite (ch058/ch059): guias irmãos para quem está migrando de fora do Next.js, não de pages para app.