Pages Router → App Router migráció napokra bontva: Next.js 16 és React 19 upgrade, /pages és /app együttélése, adatlekérdezés, Route Handlers, useRouter átvezetése és production checklist nyolc éles migráció tapasztalatával.
A Next.js Pages Router → App Router migráció egy jól megtervezett, inkrementális refaktor, amely egy közepes méretű alkalmazásnál (30–80 route) tipikusan 8–15 mérnöknapot vesz igénybe. A Next.js 16 és React 19 upgrade 1–2 nap, az /app mappa és a shared layout felállítása 1 nap, majd route-onként 1–3 óra átírás, plusz tesztelés. A Pages Router 2026-ban még nem deprecated, de minden új feature (Cache Components, Partial Prerendering, Server Actions) csak App Routeren működik, ezért a migráció halogatása technikai adósságot épít.
A /pages és /app mappa egyszerre létezhet ugyanabban a projektben, útvonalanként migrálhatsz production alatt.
Egy 50 route-os alkalmazás átállítása egy 2 fős csapatnál 2–3 hetes sprint, ebből a data fetching és metadata refaktor a legdrágább rész.
A getServerSideProps és getStaticProps teljesen megszűnik App Routerben. Helyette async Server Component és natív fetch() a cache-opciókkal.
Az _app.tsx, _document.tsx és next/head hármast a app/layout.tsx és generateMetadata váltja fel.
A useRouter viselkedése kompatibilitástörő: az next/router helyett next/navigation-t kell importálni, és a router.query szétesik három hookra.
Middleware, redirects és rewrites next.config.js-ben változatlanul működnek. Ez az egyetlen réteg, amit nem kell hozzányúlni.
Miért migrálj App Routerre 2026-ban?
2026-ban az App Router nem opció, hanem a Next.js default fejlesztési modellje. Nyolc migrációt végigvezetve a legfőbb üzleti indok mindig ugyanaz volt: az új teljesítménybeli és fejlesztői élményt javító feature-ök (Cache Components és Partial Prerendering, Server Actions, streaming Suspense boundary-k, next/after) kizárólag App Routeren működnek. A Pages Router formálisan még nincs deprecated, de a Vercel legalább két major release-en át bug-fix-only karbantartást ígért. Ez azt jelenti: új képességek nem érkeznek, a dokumentáció a példákat App Routerre írja.
A Pages Router megtartása mellett szóló érvek 2026-ban jellemzően ezek: (1) az getInitialProps-ra épülő legacy adatbetöltés, (2) egy külső CMS/preview integráció, ami még a Pages-alapú Draft Mode API-val van bekötve, (3) egy nagyméretű, teszttel gyengén lefedett kódbázis. Egyik sem blokkoló, inkább effort-becslést befolyásol. A tényleges seb: minden hónap, amit Pages-en töltesz, tovább növeli az inkompatibilis harmadik fél csomagok, saját HOC-k és _app.tsx-be épült side-effect-ek számát, amit később úgyis szét kell szedned.
Fair az alternatívákkal szemben: ha jelenlegi alkalmazásod alapvetően statikus content site (blog, dokumentáció, marketing), és nincs terved dinamikus renderelésre vagy autentikációra, a migráció ROI-ja gyenge. Érdemesebb lehet Astro-ra vagy tiszta static exportra váltani, mint App Routerbe fektetni. Minden más esetben (dashboard, SaaS, e-commerce, hitelesítés) a válasz: igen, migrálj.
Mennyi ideig tart egy Pages → App migráció?
A gyakori kérdés, hogy „mennyibe kerül", nem a route-ok számára skálázódik lineárisan, hanem a shared surface area-ra: az _app.tsx-be épült providerek, a globális HOC-k, a data fetching konvenciók és a metadata megoldás komplexitására. Az alábbi táblázat nyolc korábbi migrációm agregált adata alapján készült, TypeScript projekteknél, 2 fős dedikált csapatra vetítve.
Projekt méret
Route-ok
Upgrade + setup
Route konvertálás
Tesztelés + QA
Összesen
Kicsi (blog, marketing)
10–25
1 nap
2–4 nap
1 nap
4–6 nap
Közepes (SaaS dashboard)
30–80
2 nap
5–10 nap
1–3 nap
8–15 nap
Nagy (e-commerce, több domain)
100–300
3 nap
15–25 nap
4–7 nap
22–35 nap
Enterprise (monorepo, több app)
300+
5 nap
25–50 nap
8–15 nap
38–70 nap
A becslés két rejtett tényezőre érzékeny. Az első a tesztlefedettség: ha a kritikus flow-kra van Playwright end-to-end teszted, a QA időt körülbelül harmadolod, mert a regressziót nem manuálisan kell vadászni. A második a third-party integrációk: minden olyan csomag, ami saját _app.tsx wrappert, custom getInitialProps-t vagy Pages-specifikus HOC-t szállít (régebbi analytics, feature flag, i18n libek), 0.5–1 napot ad hozzá a becsléshez, mert vagy verzió upgrade-re, vagy alternatíva bevezetésére kényszerülsz.
Előkészületek: Next.js 16 és React 19 upgrade
Mielőtt bármelyik route-hoz hozzányúlnál, hozd fel a projektet Next.js 16-ra és React 19-re, Pages Routeren. Ez fontos szigetelési stratégia: külön PR-ban kezelheted az upgrade breaking changes-t (React 19 új refs, new ref cleanup, hydration szigorítások), és külön a router-migrációt. Ha egyszerre csinálod, két hetet fogsz debug-olni, hogy melyik változtatás okozott regressziót. (Az utolsó ügyfelemnél pontosan ez történt, és mind a kettőnknek egy egész napja rámehetett.)
Ez elvégzi a package.json-ban az upgrade-et, futtatja a codemod-okat (pl. az aszinkron params-ra átállást, ami már 15-ben elkezdődött), és riportot ír azokról a fájlokról, amikhez manuális beavatkozás kell. Egy tipikus projektnél 20–40 fájlt érint, jellemzően:
a useRouter hookok, amiket kézzel kell átvezetni (a codemod csak jelzi, mert az API-eltérés nem 1:1),
a Image komponens onLoad eseménye, ami React 19-ben szigorúbb,
bármely forwardRef használat. React 19-ben a ref már közönséges prop, a codemod ezt átírja, de a saját type definitions-ödet érdemes lektorálni.
Fusson le a build és a teszt: ha ez a lépés zöld, van egy stabil bázisod, amire biztonságosan lehet App Router route-okat rákötni.
Együttélés: /pages és /app egyszerre
A Next.js 16 App Router és Pages Router egyidejűleg működik ugyanabban a projektben. Ez az inkrementális migráció alapja: nem kell mindent egyszerre átírni, csak arra kell figyelni, hogy útvonalütközés ne legyen. Ha van /pages/dashboard.tsx és /app/dashboard/page.tsx egyszerre, App Router nyer, és a Pages route soha nem szolgálódik ki.
A stratégia, ami nálam a legjobban bevált: ne route-ról route-ra migrálj, hanem funkcionális szigetről. Egy dashboard, egy admin felület, egy blog: külön egységek, amiknek saját layoutjuk, saját providereik, saját QA ciklusuk lehet. Ez azért lényeges, mert az App Router „root layout" fogalma azt jelenti: minden /app-beli útvonalnak ugyanaz a app/layout.tsx a szülője, amit a Pages Router _app.tsx-e nem érint. Két provider fát fenntartani rövid ideig kényelmetlen, de kockázatot csökkent.
Kezdd egy zero-risk route-tal, például egy statikus „About" oldallal, aminek nincs adatbetöltése. Ez validálja a build pipeline-t, a deploy folyamatot és a QA workflow-t anélkül, hogy production traffic-ot veszélyeztetne. Utána jöhet egy közepes komplexitású olvasás-only oldal, aztán a mutáló flow-k. Ez a sorrend nem kötelező, de a mérnöki bizalom-építés miatt megéri.
Route konvertálás: page, layout, loading, error
Az App Router négy alap fájlkonvenciót vezet be minden mappában: page.tsx (route content), layout.tsx (megosztott shell), loading.tsx (Suspense fallback), error.tsx (error boundary). A Pages Router egyetlen fájlja (pl. pages/blog/[slug].tsx) tipikusan szétbomlik App Routerben egy layoutra és egy page-re.
Két lényeges változás: a params egy Promise (a Next.js 15 óta), amit await-elned kell, és a data fetching a komponensen belül történik, nincs többé külön exportált függvény. Ez az egyszerűsítés a Server Components ereje: a komponens maga a data-loader.
Adatlekérdezés migrálása: getServerSideProps és getStaticProps helyett
A Pages Router három data fetching primitivéjéből (getServerSideProps, getStaticProps, getStaticPaths) az App Routerben mindössze egyet kell megjegyezned: await fetch() egy async Server Component-ben. Ez a legnagyobb konceptuális ugrás, és a leggyakoribb hiba forrása, ha a csapat még nem ismeri az RSC data fetching mintákat.
A mapping a következő:
getStaticProps → await fetch(url, { cache: 'force-cache' }) vagy export const revalidate = N,
ISR (revalidate mező) → export const revalidate = 60 vagy a fetch-hez { next: { revalidate: 60 } },
on-demand revalidation → revalidateTag() és revalidatePath() Server Actionből vagy Route Handlerből hívva.
A gyakorlati váltás nemcsak szintaxis, hanem gondolkodásmód. Pages Routerben egy oldal egyetlen adatbetöltési kaput hívott (getServerSideProps), majd megkapta a props-ot. App Routerben minden komponens saját maga tudja lekérni az adatát: a layout hívhat egy user-lekérdezést, a page a fő tartalmat, egy beágyazott komponens a related items-et, és az RSC runtime dedupolja a request-eket ugyanahhoz a URL-hez. Ez lokalizálja a komplexitást, és megszünteti a props drill-inget.
API Routes → Route Handlers átállás
Ha van /pages/api/* mappád, azokat Route Handlers-re konvertálva /app/api/*/route.ts alakban tarthatod meg, mélyebb átírás nélkül. A régi API Routes eleve tovább működnek Pages Routeren, de ha egységesítenéd a stackot, itt a lépés. A részletekért nézd meg a külön oldalt a Route Handlers és REST API architektúráról.
A leggyakoribb minta:
// Pages API — pages/api/users/[id].ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
const user = await db.user.findUnique({ where: { id: req.query.id as string } });
if (!user) return res.status(404).json({ error: 'Not found' });
return res.status(200).json(user);
}
res.setHeader('Allow', 'GET');
return res.status(405).end();
}
// 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 } });
if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 });
return NextResponse.json(user);
}
Négy funkcionális előny: (1) minden HTTP method saját exportált függvény, nincs többé if (req.method === ...) elágaztatás, (2) a Request/Response Web API standard, azonos node-on és edge-en, (3) a body streaming natívan támogatott, (4) automatikus 405 Method Not Allowed a nem exportált method-okra. A tesztelés is egyszerűbb, mert a handler egy tiszta függvény, ami Request-et kap és Response-t ad.
_app.tsx, _document.tsx és next/head helyettesítése
Ez a lépés a Pages Router három leggyakrabban félreértett fájlját eltünteti, és cserébe egyetlen struktúrát ad. A leképzés:
A fetch() hívások itt is dedupolódnak: ugyanaz az adatlekérdezés a generateMetadata-ban és a page komponensben egyetlen upstream requestet eredményez. Ez az egyik oka annak, hogy a Metadata API architekturálisan jobb, mint a next/head: nincs többé „a metadata és a page adata két külön betöltés" probléma.
Kliens oldali navigáció: useRouter szétbontása
A next/router-t next/navigation-re kell cserélni, és ez az egyetlen olyan lépés, ahol a codemod nem tudja teljesen elvégezni a munkát, mert az API szemantikája megváltozott. A leképzés:
router.pathname → usePathname(),
router.query → useSearchParams() a query stringre + useParams() a dinamikus route paraméterekre (a Pages Router ezt egybegyúrta),
router.push('/x'), router.replace('/x'): változatlan, csak a useRouter importja jön a next/navigation-ből,
router.events (routeChangeStart stb.): megszűnt, helyette useEffect + usePathname()/useSearchParams() változás observálása vagy a NavigationEvents pattern,
router.isReady: nincs többé szükség rá, a Server Component adatai már renderelés előtt megvannak.
// Pages: pages/search.tsx
import { useRouter } from 'next/router';
export default function Search() {
const router = useRouter();
const q = router.query.q as string;
return <div>Keresés: {q}</div>;
}
// App: app/search/page.tsx ('use client' ha kliens hookot használsz)
'use client';
import { useSearchParams } from 'next/navigation';
export default function Search() {
const q = useSearchParams().get('q') ?? '';
return <div>Keresés: {q}</div>;
}
Ezen a bugon egy éles projekten estem hasra: a router.events hiánya a leggyakoribb migrációs footgun. Analytics library-k, progress bar-ok, session-timeout kezelők tipikusan erre épültek. A megoldás vagy egy dedikált 'use client' komponens a root layoutban, ami a pathname változásra reagál, vagy a library saját App Router-kompatibilis verziójára frissítés. Egy közepes projektben 3–5 komponenst érint ez a változás, csekély, de nem elhanyagolható tétel.
Middleware, redirects és rewrites
Jó hír: a middleware.ts, a next.config.js-beli redirects() és rewrites() változatlan formában működnek App Routeren. A Middleware továbbra is Edge Runtime-on fut default-ból, ugyanazokat a NextRequest és NextResponse típusokat kapja, és ugyanúgy interceptálja az összes request-et, függetlenül attól, hogy Pages vagy App route dolgozza fel őket.
Amire figyelj: a Middleware biztonsági modellje 2025-ben szigorodott a CVE-2025-29927 után. Ha bármilyen authorization logikát Middleware-ből hajtasz végre, a mélységi védelem elve most kötelező. Erről bővebben a Next.js Middleware biztonság anyagunkban.
// middleware.ts — változatlan a migráció során
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const country = request.geo?.country ?? 'US';
if (country === 'HU' && request.nextUrl.pathname === '/') {
return NextResponse.rewrite(new URL('/hu', request.url));
}
return NextResponse.next();
}
export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'] };
Ez azt jelenti: a migráció alatt a routing infrastruktúrát nem kell hozzányúlni. A redirects, a geo-based rewrites, az A/B teszt bucket assignment mind ugyanúgy működik. Ez a réteg egy stabil talppont, amire lehet építkezni.
Tesztelés és production launch checklist
Mielőtt egy route-ot /pages-ből /app-be „átbillentesz" production-ben, futtasd le a következő checklist-et. Ez a lista nyolc migrációm gyakorlati üledéke, és a leggyakoribb éles incidenseket kiszűri.
Build ellenőrzés: next build hibamentesen fut, és a route mindkét változata (ha még mindkettő létezik) szerepel a build outputban.
Type check: tsc --noEmit, a Promise-alapú params/searchParams típusok teljesen migrálva.
Cache viselkedés: production-hoz közeli környezetben (next start) ellenőrizd, hogy a route a várt render módban van (static/dynamic/ISR), a x-nextjs-cache response header segítségével.
Metadata parity: view-source-ban vagy Playwright-tel hasonlítsd össze a régi és az új oldal <head> tartalmát; title, description, canonical, og image mindenütt egyezzen.
Analytics event-ek: navigációs event-ek (page_view) az új útvonalon is regisztrálódnak; ha router.events-re épültek, most tesztelendő pontosan.
Autentikáció: a Server Component-ekben lekért user session valóban megvan minden érintett route-on, nincs „user leaks" a cache-be (érzékeny adat csak dynamic vagy cache-tag-elt fetch-ben).
Fokozatos rollout: Vercel esetén a redirect vagy Middleware alapú traffic split segítségével először 10%-nak szolgáld az új route-ot, monitorozd az error rate-et és a Core Web Vitals-t 24 órán át.
Rollback terv: ha valami félrement, a Pages route helyreállítása egyetlen deploy; ne töröld a régi fájlokat, csak nevezd át .bak-ra, amíg 1 hét stabil production futás nincs mögötted.
Egy jól strukturált migráció végén az utolsó lépés a /pages mappa teljes törlése és a hozzá tartozó dependency-k (pl. next-page-router-only csomagok) kigyomlálása. Ez általában egy fél nap: a compiler majd megmondja, mit felejtettél. Ha nem indul be a projekt, valamit még használ a Pages Router. Ne törd meg, keresd meg és fejezd be a migrációt, mielőtt véglegesítesz.
Gyakran Ismételt Kérdések
Kell egyáltalán migrálnom, ha a Pages Router alkalmazásom stabil?
Rövid távon nem, a Pages Router 2026-ban még nincs deprecated és bug-fix támogatást kap. Hosszú távon viszont minden új Next.js feature (Cache Components, Partial Prerendering, Server Actions) csak App Routeren működik, tehát a migráció halogatása egyre nagyobb versenyhátrányt jelent. Új funkciófejlesztésnél mindenképp App Routeren kezdd az új útvonalakat.
Használhatom egyszerre a Pages és App Routert?
Igen, és ez az ajánlott inkrementális migrációs stratégia. A két mappa (/pages és /app) egymás mellett létezhet ugyanabban a projektben, útvonalütközés esetén az App Router nyer. Így route-onként, funkcionális szigetenként migrálhatsz production alatt is.
Gyorsabb az App Router, mint a Pages Router?
Első betöltésre általában igen, mert az RSC csak a szükséges kliens JavaScriptet küldi el (jellemzően 30–60%-kal kisebb bundle), és a streaming Suspense hamarabb kezd rendert. Client-side navigáció ugyanaz, mindkettő client-side routing. A valós teljesítménynyereség attól függ, mennyi komponensedet tudod Server Component-té alakítani.
Mi történik a getInitialPropsszal az App Routerben?
A getInitialProps App Routerben egyáltalán nem támogatott, sem oldalakon, sem az _app.tsx-ben. Ez a legmarkánsabb breaking change. A helyettesítés a Server Component-ekben történő async data fetching (fetch() vagy adatbázis-hívás közvetlenül a komponensben). Kliens oldali adatlekérdezéshez SWR vagy TanStack Query maradhat.
Hogyan kezelem a fokozatos rollout-ot production-ben?
A legbiztonságosabb minta a Middleware alapú traffic split: cookie vagy feature-flag alapján a request egy részét az új App Router route-ra irányítod, míg a többi a régi Pages route-on marad (különböző URL prefix-szel átmenetileg). Monitorozd az error rate-et és a Core Web Vitals-t, 24–48 óra stabil futás után emelheted a százalékot.
Hogyan használj párhuzamos és lehallgatott útvonalakat a Next.js App Routerben: megosztható modálok, dashboardok és a default.js buktatói gyakorlati példákkal.