Quick Answer: Migrating to Next.js App Router#
/pages) to the modern App Router (/app) transitions your frontend architecture from client-heavy Single Page Applications (SPAs) to React Server Components (RSC). This migration replaces legacy getServerSideProps and getStaticProps with direct async Server Components, moves _app.js and _document.js into nested layout.tsx trees, replaces API routes with Route Handlers and Server Actions, and achieves up to 70% reduction in client-side JavaScript bundle sizes.
Why Migrate? Pages Router vs. App Router Architecture#
The App Router is not merely a syntactic update; it represents a complete paradigm shift in how React applications render and hydrate:
5-Step Migration Strategy (Zero Downtime)#
At Rajput Bhavin Engineering, we implement an incremental migration strategy that allows /pages and /app to co-exist simultaneously during development:
Step 1: Root Layout & Global Providers
├── Create app/layout.tsx (replaces _app.tsx and _document.tsx)
└── Configure font optimization (next/font) and global CSS
│
Step 2: Migrate Static Content & Marketing Pages
├── Convert marketing pages to pure React Server Components
└── Implement generateMetadata() for SEO & schema markup
│
Step 3: Migrate Dynamic Routes & Data Fetching
├── Replace getServerSideProps with async Server Components
└── Wrap interactive client widgets in 'use client' boundaries
│
Step 4: Modernize Data Mutations (Server Actions)
├── Replace legacy /api routes with type-safe Server Actions
└── Implement useActionState & useOptimistic for instant UX
│
Step 5: Cutover & Deprecate Legacy Pages
├── Remove redundant /pages directory files
└── Verify 100/100 Core Web Vitals across all routes
Code Comparison: Data Fetching & Mutations#
#1. Legacy Pages Router Pattern (pages/products/[id].tsx)
// Legacy: Requires complex wrapper and client-side bundle overhead
import { GetServerSideProps } from 'next';
import Head from 'next/head';
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const res = await fetch(`https://api.example.com/products/${params?.id}`);
const product = await res.json();
return { props: { product } };
};
export default function ProductPage({ product }: { product: any }) {
return (
<>
<Head>
<title>{product.name}</title>
</Head>
<h1>{product.name}</h1>
<p>{product.price}</p>
</>
);
}
#2. Modern App Router Pattern (app/products/[id]/page.tsx)
// Modern: Clean async server component with zero client-side JS overhead
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
interface Props {
params: Promise<{ id: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { id } = await params;
const product = await getProduct(id);
if (!product) return { title: 'Product Not Found' };
return {
title: `${product.name} | Rajput Bhavin Engineering`,
description: product.description,
};
}
export default async function ProductPage({ params }: Props) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<article className="p-8 max-w-4xl mx-auto">
<h1 className="text-3xl font-black text-white">{product.name}</h1>
<p className="text-lg text-foreground/70">${product.price}</p>
</article>
);
}
4 Common Migration Gotchas to Avoid#
- ✓Overusing
'use client': Placing'use client'at the top of a page file turns the entire route tree into a client bundle. Keep'use client'isolated to leaf interactive components (such as buttons, dropdowns, and modal dialogs). - ✓Accessing Request Headers in Server Components: In Next.js 15,
cookies()andheaders()are asynchronous functions (const cookieStore = await cookies()). - ✓Broken Global State Providers: Context providers (Redux, Zustand, ThemeProviders) must be wrapped inside dedicated client components (
'use client') and passed as children toapp/layout.tsx. - ✓Caching Behavior in Next.js 15: Unlike Next.js 14 (which cached
fetchby default), Next.js 15 setsfetchrequests tono-storeby default. You must explicitly specifyfetch(url, { cache: 'force-cache' })ornext: { revalidate: 3600 }.
Modernize Your Web Architecture with Rajput Bhavin Engineering#
Migrating to Next.js 15 App Router unlocks dramatic performance gains, lower cloud infrastructure costs, and superior developer ergonomics.
- ✦Learn more about our Web Development Services
- ✦Explore our SaaS Development Practice
- ✦Schedule a Codebase Architecture Audit with Rajput Bhavin today.