Next.js

Verified against Claude Code · 2026-07-23

Turn a Pages Router codebase into a phased App Router migration, not a risky rewrite

Produces a route-by-route migration plan that runs Pages Router and App Router side by side, ordered by risk, converting data-fetching methods and shared layout as it goes.

Claude CodeCursorGitHub Copilot4 fillable variables

The prompt

Ready to copy — highlighted parts are example details you can swap.

You are producing a phased migration plan from the Pages Router to the App Router for an existing codebase. Pages Router (pages/) and App Router (app/) can coexist in the same Next.js project, with Next.js resolving routes across both simultaneously, so plan this as an incremental, route-by-route migration that ships on the main branch continuously — not a big-bang rewrite done on one long-lived branch that has to land all at once.

CURRENT PAGES DIRECTORY
pages/index.tsx, pages/blog/[slug].tsx (getStaticProps), pages/dashboard.tsx (getServerSideProps, behind auth), pages/account.tsx (getInitialProps, legacy), pages/api/checkout.ts

DATA-FETCHING METHODS IN USE
getStaticProps on the blog, getServerSideProps on the dashboard, getInitialProps on the legacy account page

SHARED LAYOUT ELEMENTS
A ThemeProvider, a global Header/Footer, and a custom <Html lang> attribute in _document.tsx

TRAFFIC AND RISK PROFILE
The dashboard is the highest-traffic authenticated route and directly affects billing; the blog is low-traffic and low-risk if something briefly breaks

MIGRATION PLAN REQUIREMENTS
Order routes by risk and traffic, not alphabetically or by file size: recommend migrating low-traffic, low-risk routes first to validate the approach against this specific codebase's quirks, and the highest-traffic or most business-critical routes last, once the pattern has actually been proven rather than assumed to work. For each data-fetching method currently in use, state its App Router equivalent and the actual mental-model shift involved, not just a mechanical rename: getStaticProps becomes an async Server Component that fetches directly, with the equivalent caching behavior expressed through fetch's own cache and revalidate options rather than a dedicated special function; getServerSideProps becomes an async Server Component with a fetch call that explicitly opts out of caching, or one that reads a dynamic API like cookies() or headers() that forces the whole route to render dynamically; getInitialProps should be flagged explicitly as needing the most rework of the three, since it historically ran on both the server and the client and has no direct one-to-one App Router equivalent at all. Move shared elements out of _app.tsx and _document.tsx into app/layout.tsx, the root layout, once, early in the plan rather than per-route later — both routers can share global styles and providers through careful setup, but running duplicate providers during the transition period is a common and specifically confusing source of bugs that's worth eliminating before it has a chance to compound across multiple migrated routes. Convert pages/api routes to app/api/.../route.ts one at a time, matching whatever order the page migration is already following, rather than treating API route conversion as one separate all-at-once phase disconnected from the pages that actually call them. Call out any Pages Router-only API still in active use — next/router's useRouter behaves differently from next/navigation's, a custom _error.tsx has no direct App Router file to move into — that needs a genuine behavioral adjustment during migration, not just a file move to a new location.

OUTPUT FORMAT
A phased table: Phase | Routes and files migrated | Data-fetching conversion needed | Risk notes. Then a short "do this first, regardless of phase" list specifically for the _app.tsx and _document.tsx to root layout move.

Customize

Optional — swap in your own details for the highlighted parts above.

Why this works

The single fact that makes this migration tractable instead of terrifying is one most teams don't realize going into it: pages/ and app/ coexist in the same Next.js project, and Next.js resolves routes across both, which means the migration can genuinely happen one route at a time on the main branch instead of on a long-lived branch that has to land all at once, and a plan that doesn't lead with this fact tends to default to the riskier big-bang framing by omission rather than by an actual decision anyone made. Mapping each data-fetching method to its App Router equivalent as a mental-model shift, rather than a mechanical rename, matters because getServerSideProps and a no-store fetch inside a Server Component aren't quite the same construct wearing different syntax — the latter is one fetch call opting out of caching, while the entire component around it defaults to static unless something else forces it dynamic, which is a genuinely different way of reasoning about what makes a given render dynamic versus cached, and a migration guide that treats this as find-and-replace will produce code that compiles but doesn't behave the way the original page did. Explicitly flagging getInitialProps as the hard case, rather than letting it slot into the same conversion pattern as the other two, matters because it's structurally different — it ran on both server and client, which has no App Router analog at all — and a migration plan that doesn't call this out by name tends to discover the problem mid-migration, after the surrounding route has already been restructured around an assumption that turns out not to hold for this one file. Ordering by risk and traffic rather than by file size or alphabetical convenience is the same practical bias that shows up in any real production migration: prove the pattern, including whatever this specific codebase's own quirks turn out to be, on something that doesn't matter much before touching the route that generates revenue or gates a paying customer's dashboard.

What you get back

Phase 1: migrate pages/blog/[slug].tsx (getStaticProps to an async Server Component with a cached fetch) — low traffic, low risk, validates the pattern against this codebase's actual conventions. Phase 2: move _app.tsx's ThemeProvider and Header/Footer into app/layout.tsx once, shared by both routers going forward — done early specifically to avoid duplicate providers during Phases 3 and 4. Phase 3: pages/dashboard.tsx (getServerSideProps to a Server Component using cookies() to force dynamic rendering, since it's behind auth) — higher risk, scheduled after the pattern is proven. Phase 4 (last, highest risk): pages/account.tsx using getInitialProps — flagged for an actual rewrite rather than a mechanical conversion, since it has no direct App Router equivalent and needs to be reasoned about from scratch.

Verified against

Claude Code Sonnet 4.6 · 2026-07-23

Cursor Cursor 2.1 · 2026-07-30

Changelog

  • 2026-07-23 Initial publish, verified against Claude Code (Sonnet 4.6) on a mixed Pages/App Router Next.js 16 project.

Need this built into your business?

If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.

EXPLORE CUSTOM SOFTWARE
All Next.js prompts

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY