← Back to Blog
Architecture

Migrating an AI Export to Next.js 14 App Router: A Real-World Playbook

The Pages Router and the App Router are not interchangeable. When AI tools output a mix of both, or default to patterns that break under the App Router, the result is a build that compiles locally and crashes in production. Here is the migration playbook we run on every client codebase.

Next.js 14 ships with two routing systems: the Pages Router, which has existed since Next.js 9, and the App Router, introduced in Next.js 13 and the recommended approach for new applications. They are not interchangeable. They have different file conventions, different data fetching patterns, different component models, and different deployment behaviors. An application that mixes them without understanding the boundaries will fail in unpredictable ways.

AI tools frequently produce hybrid outputs. A codebase generated by Lovable or Bolt may contain pages/ directory files alongside app/ directory files, or App Router conventions applied to Pages Router file structures. The local development server is lenient about some of these conflicts. The production build container is not.

The migration playbook starts with a structural audit. We identify every file in the routing directories and classify it: pure App Router, pure Pages Router, or hybrid. We identify every data fetching pattern — getServerSideProps, getStaticProps, useEffect-based fetching, server components — and map them to their App Router equivalents. We identify every API route and verify its file path and handler signature match the expected convention.

The most disruptive migration is from getServerSideProps to server component data fetching. In the Pages Router, getServerSideProps runs on every request and passes props to a client component. In the App Router, server components fetch data directly and render on the server by default. The pattern looks different but the outcome is equivalent — and the App Router version is faster, simpler, and more composable.

Once the routing structure is clean, we address the client/server boundary. Every component that uses React hooks, browser APIs, or event handlers requires the 'use client' directive. Every component that can remain a server component should. The goal is to push interactivity as far toward the leaves of the component tree as possible, minimizing the JavaScript sent to the browser and maximizing server-side rendering.

Engineering Journal

Stop debugging. Start deploying.

Submit your repository in three minutes. Receive a written scope and fixed price within 12 hours.

Submit Your App for Review →