App Router vs Pages Router

Bài 2 – Phân biệt App Router và Pages Router trong Next.js

18/11/2025 DaiPhan
Bài 2 / 18

App Router vs Pages Router

Next.js cung cấp hai cơ chế định tuyến: Pages Router (cũ) và App Router (mới từ Next.js 13+). Hiểu đúng sự khác biệt là nền tảng quan trọng để tổ chức code và lựa chọn kiến trúc phù hợp.


1. App Router là gì?

App Router là cách định tuyến mới trong Next.js 13+, sử dụng thư mục app/ và cung cấp:

  • Server Components: Render ở server, giảm bundle size
  • Layouts: Shared UI giữa các pages
  • Loading states: Xử lý loading mượt mà
  • Error boundaries: Catch và handle errors gracefully
  • Streaming: Tăng perceived performance

Ý nghĩa trọng tâm:
App Router giúp xây dựng ứng dụng phức tạp với performance tốt hơn và developer experience tốt hơn.


2. Pages Router là gì?

Pages Router là cách định tuyến truyền thống trong Next.js, sử dụng thư mục pages/ với:

  • getStaticProps: Lấy data tại build-time
  • getServerSideProps: Lấy data tại request-time
  • getStaticPaths: Định nghĩa dynamic routes cho SSG
  • API Routes: Tạo API endpoints

Ý nghĩa trọng tâm:
Pages Router vẫn hoạt động tốt nhưng App Router là tương lai của Next.js.


3. So sánh chi tiết

3.1. Cấu trúc thư mục

App Router:

app/
├── layout.tsx      # Shared layout
├── page.tsx        # Route component
├── loading.tsx     # Loading state
├── error.tsx       # Error boundary
└── route.ts        # API route handler

Pages Router:

pages/
├── index.tsx       # / route
├── about.tsx       # /about route
├── api/            # API routes
└── _app.tsx        # Custom App

3.2. Data fetching

App Router (mới):

// Server Component - async function
export default async function Page() {
  const data = await fetch('https://api.example.com/data')
  const posts = await data.json()
  
  return <div>{posts.map(post => <h1>{post.title}</h1>)}</div>
}

Pages Router (cũ):

// getStaticProps hoặc getServerSideProps
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/data')
  const posts = await res.json()
  
  return { props: { posts } }
}

export default function Page({ posts }) {
  return <div>{posts.map(post => <h1>{post.title}</h1>)}</div>
}

3.3. Layout system

App Router:

// app/layout.tsx - Shared layout
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <nav>Navigation</nav>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  )
}

Pages Router:

// pages/_app.tsx - Custom App
export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

4. Khi nào dùng App Router?

✅ Nên dùng App Router khi:

  • Project mới: Bắt đầu từ đầu với Next.js 13+
  • Cần performance tốt: Server Components, streaming
  • Cần layouts phức tạp: Nested layouts, route groups
  • Cần error handling tốt: Error boundaries, not-found pages
  • Muốn latest features: Luôn được update với tính năng mới nhất

✅ Ví dụ App Router thực tế:

// app/products/[id]/page.tsx
export default async function ProductPage({ 
  params 
}: { 
  params: { id: string } 
}) {
  // Server Component - fetch data directly
  const product = await getProduct(params.id)
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <AddToCart productId={params.id} />
    </div>
  )
}

5. Khi nào dùng Pages Router?

✅ Nên dùng Pages Router khi:

  • Project cũ: Đã xây dựng với Pages Router
  • Cần stability: Đã được test kỹ lưỡng trong production
  • Team quen thuộc: Không có thời gian học App Router mới
  • Third-party libraries: Một số lib chưa support App Router
  • Documentation nhiều: Resources, tutorials phong phú

✅ Ví dụ Pages Router thực tế:

// pages/products/[id].tsx
export default function ProductPage({ product }) {
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  )
}

export async function getServerSideProps(context) {
  const product = await getProduct(context.params.id)
  
  return {
    props: { product }
  }
}

6. Migration từ Pages sang App Router

6.1. Lợi ích migration

  • Performance tốt hơn: Nhờ Server Components
  • Developer experience: Layouts, loading states, error handling
  • Future-proof: Được support và update lâu dài

6.2. Các bước migration

  1. Tạo thư mục app/ song song với pages/
  2. Chuyển từngng route một cách từ từ
  3. Test kỹ lưỡng trước khi xoá pages/
  4. Update dependencies nếu cần

6.3. Cẩn thận khi:

  • API breaking changes: Một số API khác biệt
  • Third-party libs: Kiểm tra compatibility
  • Performance: Monitor sau migration

7. Bài tập thực hành

Bài tập 1

Tạo project mới và quan sát thư mục app/ được tạo mặc định:

npx create-next-app@latest my-app

Bài tập 2

Thêm file app/about/page.tsx để tạo route /about:

export default function About() {
  return <h1>About Us - App Router</h1>
}

Bài tập 3

Tạo thư mục pages/ và file pages/legacy.tsx để test Pages Router:

export default function Legacy() {
  return <h1>Legacy Page - Pages Router</h1>
}

8. Kết luận bài học

  • App Router là tương lai của Next.js với performance và DX tốt hơn
  • Pages Router vẫn hoạt động tốt nhưng nên migrate dần
  • Dự án mới nên bắt đầu với App Router
  • Migration cần được lên kế hoạch cẩn thận

🔑 GHI NHỚ QUAN TRỌNG:

  • App Router = Server Components + Layouts + Streaming
  • Pages Router = getStaticProps/getServerSideProps + Client Components
  • Luôn ưu tiên App Router cho project mới
  • Migration cần test kỹ lưỡng và làm từ từ
18 bài học
Bài 2
Tiến độ hoàn thành 11%

Đã hoàn thành 2/18 bài học