Capítulo 57 de 456
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.
app and pages directories coexist; you migrate route by route while keeping the rest on pages.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.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 getStaticPaths — true (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.ts): Replace pages/api/*, built on Web Request/Response APIs.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.// app/layout.tsx — required root layout
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// 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>
)
}
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} />
}
generateStaticParams retorna array plano de segmentos, mais simples que getStaticPaths.pages Directory | app Directory | Route |
|---|---|---|
index.js | page.js | / |
about.js | about/page.js | /about |
blog/[slug].js | blog/[slug]/page.js | /blog/post-1 |
| pages API | app equivalent |
|---|---|
getServerSideProps | fetch(url, { cache: 'no-store' }) em Server Component |
getStaticProps | fetch(url) (default force-cache) |
getStaticProps com revalidate | fetch(url, { next: { revalidate: N } }) |
getStaticPaths | generateStaticParams |
getStaticPaths fallback | dynamicParams (route segment config) |
pages/_app.js + pages/_document.js | app/layout.js (root layout) |
pages/_error.js | error.js |
pages/404.js | not-found.js |
pages/api/* | route.js (Route Handlers) |
next/head | Metadata API (export const metadata) |
useRouter de next/router | useRouter/usePathname/useSearchParams de next/navigation |
_app/_document apagando os originais antes de terminar: quebra as rotas restantes em pages/*; manter ambos até a migração completa.next/head dentro de app: não funciona; usar a Metadata API (export const metadata).<Link> cruze routers automaticamente: navegação entre App Router e Pages Router é sempre hard navigation.app.page.tsx Server Component.getServerSideProps/getStaticProps) para fetch() com opções de cache/next.revalidate direto no componente async._app.js; podem ser importados em qualquer layout/page/componente do app.next-image-to-legacy-image, new-link, etc.) automatizam boa parte do trabalho mecânico de upgrade.use cache no lugar dos route segment configs.pages para app.