Next.js App Router vs Pages: when I still touch Pages

Table of Contents
Table of Contents
I start new Next.js work in App Router. I still touch Pages on old repos, on mixed trees, and anywhere getServerSideProps still owns the request.
That is the comparison. Not a feature matrix.
I’m Aris Setiawan. I ship Next.js for client work. Yesterday I wrote the App Router I actually keep in `app/`. This post is the other question: when pages/ is still on the disk, and what I do not rewrite in a weekend.
The offer page is Hire a Next.js developer. This is the practice, not a migration sales deck.
New product: I do not open pages/
A 2026 product starts in app/. Layout, page, a small client boundary, a cache I can walk after a write. I wrote that set yesterday. I do not add a pages/ folder so the repo looks familiar.

Pages still exists in Next.js. That is not a reason to start there. _app, getServerSideProps, and pages/api are the old request path. On a new app they become a second tree I have to explain on day two.
If the ticket is a new product, the router is App Router. If someone wants Pages because the last contractor used it, I say no and I keep the reason short.
Old repo: pages/ is still the tree
I inherit apps that live in pages/. _app.js, index.js, pages/api, a pile of getServerSideProps. The product works. Users are on it.

I do not rewrite that tree because a post said App Router is the only router. I open the data function first. I write down where the session is read, what the page fetches, and what happens after a write. If I cannot say those three things, I am not ready to move the route.
I still touch Pages to ship a fix: a broken query, a header, a form that posts to the wrong API. The fix stays in Pages if the route is Pages. Mixing one App Router file into a Pages-only tree without a plan is how you get two auth stories.
Mixed repo: both folders, one URL at a time
A lot of client repos already have app/ and pages/ next to each other. New marketing pages landed in App Router. The dashboard is still getServerSideProps.

That is fine if each URL has one owner. It is not fine if /account is Pages and /account/settings is App Router and they do not share the session the same way.
When I migrate, I take one URL. I copy the data rules, not the file names. getServerSideProps does not become a Server Component by renaming the file. Cookies, cache, and the client boundary have to be decided again. I ship that URL. I leave the next one until this one is boring.
I do not pick a weekend and “finish the migration.” That is how you get a half-moved checkout and a rollback you cannot explain.
What I still meet in Pages
getServerSideProps: the request is the cache. I do not pretend fetch with a tag is the same thing. If the page must see the cookie on every hit, I keep it that way until the App Router version says so in the fetch.
getStaticProps plus revalidate: this one maps. A Server Component with a revalidate window or a tag is the replacement. I still have to name what busts it after a write.
_app and _document: layout and the document shell. In App Router that is app/layout.tsx and a small metadata file. I do not paste _app into a client layout.
pages/api: route handlers in App Router, or a Server Action when the work is ours. I do not open a new pages/api file on an app that already has app/.
What I skip
Starting a 2026 product in Pages.
Flipping a root layout to 'use client' so the Pages habits fit.
A rewrite with no rollback URL. If I cannot keep the old route live while the new one is proven, I am not migrating yet.
Treating “App Router” on a job post as the architecture. The folder on disk is the architecture.
What I will take
I take the messy case: old pages/, a mixed tree, a route that still needs getServerSideProps until we can walk the cache. I will also take a new app/ tree. I will not take a rewrite of the whole site with no owner for each URL.
If you want this owned on your product, that is the work on Hire a Next.js developer. Send the repo or the ticket.



