hardFull Stack EngineerTechnology
How does Next.js App Router handle server-side rendering, static generation, and client-side navigation differently from the Pages Router?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
Interviewer asked:
> "We're migrating from Pages Router to App Router. Can you explain the fundamental differences in how pages are rendered and routed? What are the gotchas we should watch for?"
> "We're migrating from Pages Router to App Router. Can you explain the fundamental differences in how pages are rendered and routed? What are the gotchas we should watch for?"
Suggested Solution
Rendering Strategies Comparison
Pages Router
// pages/products/[id].js
// Static: runs at build time
export async function getStaticProps({ params }) {
return { props: { product: await getProduct(params.id) } };
}
// Dynamic: runs on every request
export async function getServerSideProps({ params }) {
return { props: { product: await getProduct(params.id) } };
}
App Router
// app/products/[id]/page.tsx
// Default = Server Component = SSR
// No special exports needed!
async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <ProductCard product={product} />;
}
// Static generation:
export const dynamic = 'force-static'; // or generateStaticParams
export async function generateStaticParams() {
const products = await getAllProducts();
return products.map(p => ({ id: p.id }));
}
// ISR:
export const revalidate = 3600; // revalidate every hour
Key Architectural Differences
layout.tsxloading.tsxerror.tsx per segmentpages/api/app/api/Layout Persistence (Biggest UX Win)
// app/layout.tsx — persists across navigation
export default function RootLayout({ children }) {
return (
<html>
<body>
<Navbar /> {/* Doesn't re-render on navigation */}
{children}
</body>
</html>
);
}
// app/dashboard/layout.tsx — nested layout
export default function DashboardLayout({ children }) {
return (
<div className="flex">
<Sidebar /> {/* Persists within dashboard routes */}
{children}
</div>
);
}
Migration Gotchas
1. "use client" boundary — Forgetting this causes "useState is not defined" errors2. No
getServerSideProps — Data fetching is now direct async in the component3. Metadata API — Replace
<Head> with metadata export or generateMetadata4. Route params — Now passed as props, not
useRouter().query5. Middleware — Runs on Edge Runtime, not all Node.js APIs available