Migrare de la Pages Router la App Router în Next.js 16: Playbook Complet cu Estimări
Playbook practic de migrare de la Pages Router la App Router în Next.js 16: pași, estimări realiste (8-14 zile), tabel de echivalențe și exemple de cod din producție.
Migrarea de la Pages Router la App Router în Next.js 16 este un proces incremental care înlocuiește pages/ cu app/, convertește getServerSideProps și getStaticProps în Server Components asincrone, transformă _app.tsx și _document.tsx într-un layout, iar rutele API se mută în Route Handlers. Am migrat șase echipe de producție de la Pages Router în ultimele optsprezece luni. Din experiența mea, o aplicație de complexitate medie (30–60 de pagini) cere între 8 și 14 zile de lucru concentrat, cu Pages Router și App Router rulând simultan pe parcursul tranziției.
App Router și Pages Router coexistă în aceeași aplicație, deci poți migra o rută pe zi fără să oprești producția.
getServerSideProps devine cod scris direct în componenta async function Page(), iar getStaticProps se traduce în fetch() cu cache: 'force-cache'.
Fișierele _app.tsx și _document.tsx se combină într-un singur app/layout.tsx, care conține tag-urile <html> și <body>.
Rutele API din pages/api/ se mută în app/api/*/route.ts, cu exporturi numite (GET, POST) în loc de export default.
Meta tag-urile din next/head se înlocuiesc cu Metadata API, static sau dinamic prin generateMetadata.
Estimare realistă: 8–14 zile pentru 30–60 de pagini. Adaugă 3–5 zile pentru fiecare integrare complexă (auth, i18n, tema).
Care este diferența dintre Pages Router și App Router?
Pages Router (introdus în 2016) este sistemul de rutare bazat pe fișiere din directorul pages/, în care fiecare fișier .tsx este o rută, iar datele se preîncarcă prin funcțiile speciale getServerSideProps, getStaticProps și getStaticPaths. Totul este, implicit, o Client Component. Adică se rulează în browser și îngroașă bundle-ul de JavaScript.
App Router (stabil din Next.js 13.4, matur în Next.js 16) folosește directorul app/, în care fișierele au roluri semantice: page.tsx pentru rută, layout.tsx pentru shell-uri partajate, loading.tsx pentru Suspense boundaries, error.tsx pentru error boundaries. Componentele sunt Server Components by default, ceea ce înseamnă că fetch() se face pe server, secretele rămân pe server, iar HTML-ul ajunge deja randat la client.
În aceeași aplicație, ambele routere coexistă pașnic. Dacă ai o rută /dashboard în pages/dashboard.tsx și creezi app/dashboard/page.tsx, App Router are prioritate. Asta e mecanica care face migrarea sigură: ștergi fișierul din pages/ doar după ce echivalentul din app/ funcționează în producție.
Cât durează migrarea de la Pages Router la App Router?
În experiența mea de migrare a șase echipe, estimarea depinde brutal de trei factori: numărul de pagini, complexitatea layout-urilor globale și adâncimea integrărilor terțe (auth, i18n, feature flags, analytics).
Aplicație mică (10–25 pagini, un layout global): 4–7 zile de lucru pentru un singur developer senior.
Aplicație medie (30–60 pagini, dashboard cu autentificare): 8–14 zile, sau 2 sprint-uri cu doi developeri.
Aplicație mare (100+ pagini, i18n, A/B testing, CMS custom): 4–8 săptămâni pentru o echipă de trei.
Adaugă un buffer de 20% pentru edge cases neanticipate: biblioteci care rulează doar în client (Framer Motion, react-dnd), integrări cu getInitialProps depreciat, sau layout-uri care depind de context providers încurcați. Am migrat un dashboard SaaS de 45 de rute în 11 zile lucrătoare, dar unul cu i18n bazat pe next-i18next a durat 19 zile fiindcă a trebuit să înlocuim biblioteca cu next-intl.
Tabel de echivalențe: Pages Router vs App Router
Ține deschis acest tabel pe al doilea monitor pe parcursul migrării. Este cel mai des consultat cheat sheet din procesul meu:
Concept
Pages Router
App Router (Next.js 16)
Directorul de rute
pages/
app/
Fișierul rutei
pages/about.tsx
app/about/page.tsx
Layout global
pages/_app.tsx + _document.tsx
app/layout.tsx
Data fetching (SSR)
getServerSideProps
async function Page() + fetch()
Data fetching (SSG)
getStaticProps
fetch(url, { cache: 'force-cache' })
Parametri dinamici
getStaticPaths
generateStaticParams
Rute API
pages/api/user.ts
app/api/user/route.ts
Meta tag-uri
<Head> din next/head
export const metadata
Componentă implicită
Client Component
Server Component
Formulare mutații
POST către pages/api/
Server Actions
Migrare pas cu pas: procesul în șapte etape
Sincer, iată ordinea pe care o urmez la fiecare migrare, testată pe șase proiecte de producție. Nu sări peste etape, chiar dacă pare tentant. Ordinea previne conflicte de rutare și minimizează durata reglării fine.
1. Ridică baseline-ul: actualizează la Next.js 16
Înainte să atingi app/, urcă la Next.js 16 pe Pages Router-ul existent. Rezolvă tot ce se strică: deprecări React 19, Turbopack activat, tipuri noi. Rulează testele. Deployează. Doar apoi treci mai departe.
Alege o rută simplă, fără dependențe: /about, /contact, sau /terms. Creează app/about/page.tsx, mută conținutul, șterge pages/about.tsx. Testează în producție. Rezolvă surprizele.
4. Portează utilitățile partajate
Context providers, teme, analytics: mută-le în app/providers.tsx (un Client Component wrapper) și include în layout. Aici trebuie să separi cu grijă cod client de cod server.
5. Migrează rutele statice
Paginile fără data fetching, sau cele cu getStaticProps simplu, sunt câștigurile ușoare. Lasă rutele autentificate și pe cele cu SSR complex la sfârșit.
6. Migrează rutele dinamice și API-urile
Aici ordinea contează: migrează API-urile înainte de rutele care le apelează, dacă schimbi și schema. Altfel, migrează în ordine inversă (rute apoi API-uri) pentru izolare.
7. Șterge dependințele Pages Router
Când pages/ conține doar _app.tsx și _document.tsx, elimină și pe acestea. Șterge getInitialProps, orice referință la next/router înlocuită de next/navigation.
Migrarea getServerSideProps și getStaticProps
Cea mai frecventă întrebare pe care o primesc: „unde se duce getServerSideProps?" Răspunsul scurt: dispare. Codul din interior se mută direct în corpul componentei, care devine async.
Pentru getStaticProps, înlocuiește cache: 'no-store' cu cache: 'force-cache' (implicit în Next.js 16 pentru fetch în build). Pentru getStaticPaths, folosește generateStaticParams pentru ISR și rute dinamice. Sintaxa e similară, dar rulează pe rutele din app/.
Migrarea _app.tsx și _document.tsx în layout-uri
Cele două fișiere globale din Pages Router se combină într-unul singur în App Router. Iată cum arată tranziția:
Separarea în două fișiere nu e stilistică, e obligatorie. Layout-ul rulează pe server, iar ThemeProvider folosește useState intern, așa că are nevoie de 'use client'. Pattern-ul e atât de universal încât îl scriu automat la începutul fiecărei migrări. Dacă folosești și autentificare, aruncă un ochi pe ghidul de Auth.js v5 în Next.js 16 cu sesiuni și roluri înainte să pui providerii.
Cum convertești rutele API în Route Handlers?
Rutele din pages/api/*.ts devin app/api/*/route.ts, cu exporturi numite pentru fiecare metodă HTTP. Nu mai există export default (req, res) =>. Consultă ghidul complet Route Handlers pentru API REST pentru pattern-uri avansate.
// app/api/users/[id]/route.ts
import { NextResponse } from 'next/server'
export async function GET(
req: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
const user = await db.user.findUnique({ where: { id } })
return NextResponse.json(user)
}
export async function DELETE(
req: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
await db.user.delete({ where: { id } })
return new NextResponse(null, { status: 204 })
}
Codul devine mai declarativ. Fiecare metodă HTTP este o funcție separată, semnătura este Web Fetch standard (Request, Response), și nu mai există branch-uire manuală pe req.method. Cei care vin din Remix sau Hono se vor simți acasă.
Migrarea next/head în Metadata API
Tag-urile <Head> din next/head se înlocuiesc cu Metadata API static (obiect exportat) sau dinamic (funcție generateMetadata). API-ul suportă native Open Graph, Twitter cards, canonical URLs, alternates i18n, robots. Practic tot ce configurai manual.
Metadata static
// app/blog/page.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Blog | Numele Site-ului',
description: 'Articole despre Next.js și React.',
openGraph: {
title: 'Blog',
images: ['/og-blog.png'],
},
}
Incremental. Întotdeauna. În cinci din șase migrări pe care le-am făcut, echipa a încercat inițial abordarea „big bang" (branch lung, tot deodată), și în trei din cinci am oprit-o și am refăcut incremental după prima săptămână de conflicte de merge.
Abordarea incrementală funcționează pentru că App Router și Pages Router coexistă. Poți avea /dashboard în App Router și /settings în Pages Router în același build. Livrezi câte o rută pe deploy, monitorizezi metricele Real User Monitoring, faci rollback dacă ceva se rupe.
Ordinea mea preferată: (1) rute statice publice (about, pricing), (2) landing pages (home, categorii), (3) rute autentificate simple, (4) dashboard-uri complexe, (5) rute API, (6) ștergere pages/. Așa reduci riscul pe fluxurile care generează venit.
Capcane frecvente și cum le eviți
După șase migrări, aceleași cinci probleme apar de fiecare dată. Le enumăr ca să le eviți încă din prima săptămână.
1. „use client" viral
Când o componentă are 'use client', tot arborele importat devine client. Am văzut echipe care puneau 'use client' pe layout și pierdeau avantajul Server Components. Regulă simplă: pune 'use client' cât mai jos posibil în arbore, pe frunze (butoane cu onClick, formulare cu useState).
2. Uitarea că params este Promise
În Next.js 16, params și searchParams sunt Promise. Accesul direct params.id (fără await) generează warning-uri acum și va deveni eroare hard. Folosește const { id } = await params.
3. Import greșit al router-ului
next/router nu funcționează în App Router. Trebuie next/navigation: useRouter, usePathname, useSearchParams. API-ul e diferit; router.push('/x') merge, dar router.query nu există.
4. Cookie-uri și headere
În Pages Router accesai cookie-urile prin ctx.req.cookies. În App Router folosești cookies() din next/headers, iar în Next.js 16 e async. Similar pentru headers().
5. Middleware devenit proxy
În Next.js 16, middleware.ts a fost redenumit proxy.ts pentru claritate. Vezi ghidul complet de migrare de la middleware la proxy.ts. Nu uita să redenumești fișierul și să actualizezi importurile. Funcțiile NextRequest și NextResponse rămân la fel.
Întrebări frecvente
Trebuie să migrez de la Pages Router la App Router?
Nu obligatoriu. Pages Router este încă suportat oficial în Next.js 16 și nu are dată anunțată de deprecare. Migrează dacă ai nevoie de Server Components, Server Actions, layout-uri partajate cu state persistent, sau streaming granular. Pentru aplicații mici cu SSR simplu, Pages Router e perfect funcțional.
Pot rula Pages Router și App Router în același timp?
Da, coexistă în aceeași aplicație. Dacă ai o rută atât în pages/ cât și în app/, App Router are prioritate. Asta face migrarea incrementală sigură: muți câte o rută pe deploy și verifici în producție înainte să ștergi versiunea veche.
Ce se întâmplă cu getInitialProps în App Router?
getInitialProps nu este suportat în App Router. Trebuie să rescrii logica ca async function Page() cu fetch, sau, dacă rulai cod client-side inițial, să folosești useEffect într-o Client Component. În Pages Router rămâne funcțional, dar deja este marcat legacy.
Sunt codemod-urile oficiale suficiente pentru migrare?
Nu. Codemod-urile automatizează transformări sintactice (imports, redenumiri de props), dar nu convertesc arhitectura. Trebuie să decizi manual ce e Server Component, ce e Client Component, cum se împart layout-urile, cum se migrează data fetching. Economisesc timp, nu iau decizii.
Cât efect are migrarea asupra SEO?
Neutru sau pozitiv dacă păstrezi URL-urile identice și implementezi Metadata API corect. App Router livrează HTML mai rapid (Server Components streaming), ceea ce îmbunătățește Largest Contentful Paint. Cel mai mare risc SEO: URL-urile care se schimbă accidental. Verifică fiecare rută migrată cu curl -I înainte de deploy.
Ghid complet Metadata API în Next.js 16: static vs generateMetadata, OG images dinamice cu ImageResponse, sitemap.ts, robots.ts, JSON-LD pentru rich snippets, canonical URLs și i18n. Cu exemple de cod și erori comune întâlnite în producție.
Ghid practic pentru generateStaticParams în Next.js 16: cum funcționează dynamicParams, când folosești ISR și cum invalidezi cache-ul on-demand cu revalidatePath și revalidateTag.
Ghid practic pentru Turbopack în Next.js 16: cum activezi build-urile de producție, migrezi de la Webpack, configurezi loadere SVG/GraphQL, filesystem caching în CI și Subresource Integrity nativ. Cu benchmark-uri reale și capcane din producție.