DEV Community

Cover image for Next.js Pages Router to App Router Migration Guide (2026)
TheKitBase
TheKitBase

Posted on • Originally published at thekitbase.app

Next.js Pages Router to App Router Migration Guide (2026)

Migrating from Next.js Pages Router to App Router is a significant but manageable project. The recommended approach is incremental - both routers can run in parallel in the same Next.js project, so you migrate route by route rather than all at once.

Next.js App Router was introduced in Next.js 13.4 and became stable and recommended in Next.js 14. By 2026, nearly all new Next.js projects use App Router. If you're on Pages Router, here's everything you need to know about migrating.

Key differences

Concept Pages Router App Router
File convention pages/index.tsx app/page.tsx
Layout _app.tsx wraps all pages layout.tsx per segment
Data fetching getServerSideProps / getStaticProps async Server Components
Client components All client by default Server by default, 'use client' to opt in
Metadata next/head in each page metadata export
API routes pages/api/route.ts app/api/route/route.ts
Loading states Manual loading.tsx convention
Error boundaries Manual error.tsx convention
Streaming Not supported Built-in with Suspense

Step 1: Upgrade Next.js

npm install next@latest react@latest react-dom@latest
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the app directory

Both routers coexist. Create an app directory - Next.js serves routes from app when available, falling back to pages for anything not yet migrated.

/app
  layout.tsx      # Required root layout
  page.tsx        # Replaces pages/index.tsx when ready
/pages
  index.tsx       # Still works until removed
  about.tsx       # Unmigrated pages keep working
  api/
    auth.ts       # API routes in pages/api still work
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the root layout

// app/layout.tsx
import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
  title: { default: "My App", template: "%s | My App" },
  description: "My app description",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script dangerouslySetInnerHTML={{ __html: `
          (function() {
            var s = localStorage.getItem('theme');
            var sys = matchMedia('(prefers-color-scheme: dark)').matches;
            if (s === 'dark' || (!s && sys)) document.documentElement.classList.add('dark');
          })();
        `}} />
      </head>
      <body className="bg-background text-foreground">{children}</body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Migrate pages one at a time

// Before: pages/about.tsx
export default function AboutPage() {
  return <div>About page</div>;
}

// After: app/about/page.tsx
export const metadata = { title: "About" };

export default function AboutPage() {
  return <div>About page</div>;
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Migrate data fetching

// Before: Pages Router
export async function getServerSideProps({ params }) {
  const post = await fetchPost(params.id);
  return { props: { post } };
}
export default function PostPage({ post }) {
  return <div>{post.title}</div>;
}

// After: App Router
export default async function PostPage({ params }: { params: { id: string } }) {
  const post = await fetchPost(params.id);
  return <div>{post.title}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Common migration issues

  • useState/useEffect in Server Components - add "use client" directive
  • next/router vs next/navigation - App Router uses useRouter from next/navigation with a different API
  • getStaticPaths → generateStaticParams - function name and return format changed
  • API routes - move from pages/api/route.ts to app/api/route/route.ts with named exports (GET, POST)
  • next/head → metadata export - export a metadata object from page.tsx instead

Frequently asked questions

Should I migrate from Pages Router to App Router in 2026?

For active projects, yes. App Router has better performance through React Server Components and simpler data fetching. For stable projects not being actively developed, the migration cost may not be worth the benefit. New projects should always start with App Router.

Can Pages Router and App Router run together?

Yes. This is the recommended migration strategy. Both routers work simultaneously - routes in app/ take precedence over the same routes in pages/. Migrate one route at a time.


Originally published at thekitbase.app

Top comments (0)