Capítulo 311 de 456
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.
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.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.page.js: pages/about.js → app/about/page.js; pages/blog/[slug].js → app/blog/[slug]/page.js.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.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).getStaticPaths → generateStaticParams: retorna array de objetos de segmento ([{ id: '1' }, { id: '2' }]) em vez de { params: {...} }; pode ser usado em layouts.fallback → dynamicParams: 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.pages/api/* continua funcionando; em app, o equivalente é app/api/route.ts exportando GET/POST/etc usando Request/Response nativos da Web.pages, CSS global só podia entrar em _app.js; em app, qualquer layout/page/componente pode importar CSS global.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>
}
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} />
}
generateStaticParams no lugar de getStaticPaths, com fetch de dados direto no componente.pages Directory | app Directory |
|---|---|
index.js | page.js |
about.js | about/page.js |
blog/[slug].js | blog/[slug]/page.js |
getServerSideProps | fetch(url, { cache: 'no-store' }) |
getStaticProps | fetch(url) (default force-cache) |
getStaticProps + revalidate | fetch(url, { next: { revalidate: N } }) |
getStaticPaths | generateStaticParams |
fallback: true/false/'blocking' | dynamicParams = true/false |
pages/_app.js + pages/_document.js | app/layout.js (root layout) |
pages/_error.js | error.js |
pages/404.js | not-found.js |
pages/api/* | app/**/route.js |
_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.useRouter de next/router em Client Component dentro de app: não é suportado; precisa ser next/navigation.app e por pages é hard navigation, sem prefetch automático.pages e app coexistindo.app.fetch() com opções de cache._app+_document numa peça só.app directory como destino.next: { revalidate } no App Router é o equivalente direto do revalidate do Pages Router.