Next.js Page Router vs App Router: Modern API Structures Explained (2025)

NextJS
App-v/s-Page
API Router
Nested Routes
Dynamic Routing
Route Groups
Fallback Routing
Next.js has revolutionized web development with its hybrid static & server-rendered architecture. Traditionally, it followed a simple file-based routing mechanism known as the Page Router (/pages). However, with the introduction of App Router in Next.js 13+, a more powerful and flexible approach to routing and APIs was introduced.
πΉ Section 1: What is Routing in Next.js?
Routing in Next.js controls how users navigate between different pages and how the application handles requests.
β What is Page Router?
The Page Router has existed since the beginning of Next.js. It uses a file-based system inside the /pages directory. Every .js, .ts, or .tsx The file inside this folder automatically becomes a route.
Example:
1/pages/index.tsx β '/'
2/pages/about.tsx β '/about'
This also includes API routes:
1/pages/api/user.ts β '/api/user'
β How Page Router Handles API Requests:
In the /pages/api folder, each file becomes a route, and the default export is a handler function.
Example:
1// pages/api/hello.ts
2export default function handler(req, res) {
3 if (req.method === 'GET') {
4 res.status(200).json({ message: "Hello from Page Router API" });
5 }
6}
πΉ Section 2: Introduction to App Router
In Next.js 13+, Vercel introduced the App Router, a new way to structure applications around React Server Components.
Instead of placing files in /pages App Router uses the /app directory. It allows layouts, server-side streaming, loading/error UI components, and modern API route handling with route.ts.
β Example Folder:
1/app
2 /api
3 /hello
4 route.ts
5 /dashboard
6 /settings
7 page.tsx
β How App Router Handles API:
1// app/api/hello/route.ts
2export async function GET() {
3 return new Response(JSON.stringify({ message: "Hello from App Router" }), {
4 status: 200,
5 });
6}
β Benefits of App Router:
- Granular control with layouts.
- React Server Components.
- Better streaming and performance.
- Built-in support for loading and error UI.
πΉ Section 3: Key Differences Between Page Router and App Router
Feature | Page Router | App Router |
---|---|---|
Routing Directory | /pages | /app |
Routing System | File-based | Component + file-based |
API Routes | /pages/api/*.ts | /app/api/*/route.ts |
HTTP Method Handlers | Single function handler | Individual functions per HTTP method |
Layout Support | Limited | Full support for nested layouts |
React Server Components | β | β |
Streaming Support | β | β (Suspense, streaming) |
Loading UI | Custom or manual | Built-in via loading.tsx |
Error Handling | _error.tsx | error.tsx per segment |
Middleware Support | β | β |
SEO Meta Management | Manual | Built-in with layouts and <Head> |
πΉ Section 4: Real-World API Route Comparison
β Page Router Example: GET blog posts
1// pages/api/blogs.ts
2export default function handler(req, res) {
3 if (req.method === "GET") {
4 res.status(200).json({ posts: ["Post1", "Post2"] });
5 } else {
6 res.status(405).end(); // Method Not Allowed
7 }
8}
β App Router Version:
1// app/api/blogs/route.ts
2export async function GET() {
3 return Response.json({ posts: ["Post1", "Post2"] });
4}
β App Router: Full CRUD
1export async function POST(req: Request) {
2 const body = await req.json();
3 return new Response(JSON.stringify({ created: body }), { status: 201 });
4}
5
6export async function PUT(req: Request) {
7 const body = await req.json();
8 return new Response(JSON.stringify({ updated: body }));
9}
10
11export async function DELETE(req: Request) {
12 return new Response("Deleted successfully", { status: 204 });
13}
πΉ Section 5: When Should You Use Each Router?
β Page Router:
- Simpler apps or websites.
- When backward compatibility matters.
- Learning or prototyping.
β App Router:
- Enterprise-level applications.
- Need for layouts, streaming, and server rendering.
- Want modular, scalable APIs.
πΉ Section 6: Migrating from Page Router to App Router
β Step-by-Step Plan:
- Create a new /app directory.
- Slowly port your pages from /pages to /app.
- Replace /pages/api routes with /app/api/.../route.ts.
- Use layout.tsx, loading.tsx, error.tsx Where necessary.
- Test and clean up /pages.
β Common Pitfalls:
- Donβt forget to update the dynamic route syntax (e.g., [id].tsx β [id]/page.tsx).
- Handle React Server Component restrictions (canβt use useState, etc.).
πΉ Section 7: Best Practices for API Route Management
β Folder Naming:
Use RESTful naming conventions.
1/app/api/posts/route.ts
2/app/api/users/[id]/route.ts
β HTTP Method Isolation:
Separate logic by method.
1export async function GET(req: Request) { /* Read */ }
2export async function POST(req: Request) { /* Create */ }
β Use Utility Functions:
Organize logic into shared files:
1// lib/db.ts
2export const getDB = async () => { ... };
3
4// app/api/user/route.ts
5import { getDB } from "@/lib/db";
πΉ Section 8: Performance Considerations
- App Router uses server rendering by default = reduced bundle size.
- Uses React streaming + Suspense.
- Optimized for performance-first architecture.
β Tips:
- Use cache and revalidate options in fetch.
- Optimize layouts to minimize re-renders.
- Use loading.tsx to reduce LCP (Largest Contentful Paint).
πΉ Section 9: SEO Handling in App vs Page Router
β Page Router SEO:
1import Head from 'next/head';
2
3<Head>
4 <title>Home | My Site</title>
5 <meta name="description" content="..." />
6</Head>
β App Router SEO:
1// app/page.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5 title: 'Home | My Site',
6 description: 'This is the homepage',
7};
This supports:
- Dynamic OG tags
- Open Graph images
- SEO at the layout or page level
πΉ Section 10: The Future of Routing in Next.js
- Page Router is still supported, but App Router is the recommended standard going forward.
- New features include App Router.
- If starting a new project, use App Router.
πΉ Section 11: Dynamic API Routes in Page Router vs App Router
Dynamic API routes allow you to capture parameters from the URL and handle requests accordingly. This is useful for CRUD operations, user profiles, blog post details, etc.
β Page Router: Dynamic API Routes
In the Page Router (/pages/api), dynamic routes are created by wrapping the filename in square brackets ([]).
π File Structure:
1/pages/api/user/[id].ts
π¦ Example Code:
1 // pages/api/user/[id].ts
2export default function handler(req, res) {
3 const { id } = req.query;
4
5 if (req.method === 'GET') {
6 res.status(200).json({ userId: id });
7 } else {
8 res.status(405).end(); // Method Not Allowed
9 }
10}
β Key Points:
- req.query It is used to get dynamic values like id.
- You need to handle HTTP methods manually (GET, PUT, etc.).
- Route: /api/user/123 will return { userId: "123" }.
β App Router: Dynamic API Routes
In the App Router, dynamic routes are created using folder names wrapped in square brackets.
π File Structure:
bashCopyEdit
1/app/api/user/[id]/route.ts
π¦ Example Code:
1// app/api/user/[id]/route.ts
2export async function GET(request: Request, context: { params: { id: string } }) {
3 const { id } = context.params;
4
5 return Response.json({ userId: id });
6}
β Key Points:
- context.params Provides access to the dynamic segment.
- Each method (GET, POST, etc.) is handled as a separate exported function.
- Route: /api/user/123 will return { userId: "123" }.
π§ Comparison Table:
Feature | Page Router | App Router |
---|---|---|
Folder/File | /pages/api/user/[id].ts | /app/api/user/[id]/route.ts |
Access Param | req.query.id | context.params.id |
HTTP Method Separation | Single handler with if blocks | Exported per-method functions (GET, POST) |
Code Reusability | Needs custom helpers | Cleaner structure using isolated functions |
π Bonus: Handling POST with Dynamic ID (App Router)
1// app/api/user/[id]/route.ts
2export async function POST(request: Request, context: { params: { id: string } }) {
3 const { id } = context.params;
4 const body = await request.json();
5
6 return Response.json({
7 message: `Data updated for user ${id}`,
8 newData: body,
9 });
10}
β Best Practices
- π Folder Clarity: Always keep dynamic API routes in clearly named folders ([id], [slug]).
- π§Ό Validate Input: Use zod or custom logic to validate dynamic route params.
- π Secure: Protect against invalid or unauthorized access using middleware or logic checks.
- π Cache control: Use headers like Cache-Control or Next.js revalidate where needed.
π― Conclusion
Both routing systems serve their purpose. However, as we move deeper into 2025, the App Router is the future of Next.js development, offering unmatched flexibility, speed, and developer experience.
Understanding their API route structures, limitations, and best practices gives developers the confidence to build high-performance web applications.