C.W.K.
Stream
Lesson 04 of 08 · published

App Router vs Pages Router

~22 min · App Router, Pages Router, migration

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Two routers, one repo — for now

Next.js still ships both routers because legacy apps haven't all migrated. New work belongs in the App Router (app/ directory). The Pages Router (pages/) exists for migration and won't see new feature work.

CapabilityPages RouterApp Router
Directorypages/app/
Default render targetClientServer
Data fetchinggetServerSideProps / getStaticPropsasync components, direct fetch
LayoutsManual _app.tsx wraplayout.tsx per segment
Loading UIManualloading.tsx + Suspense
Error UI_error.tsxerror.tsx + global-error.tsx
StreamingLimitedFirst-class
Server ActionsNoYes

The mental model shift

Pages Router thinks in pages with separate data-fetching functions. App Router thinks in composable Server & Client Components organized by route segments. Most "Next.js feels weird now" reactions are about this shift, not about syntax.

Coexistence rules

Both routers work in the same project, segment by segment. If a path exists in both app/ and pages/, App Router wins. Migrate route by route; you don't need a big-bang rewrite.

Code

Pages Router — separate data function·tsx
// pages/blog/[slug].tsx
export async function getServerSideProps({ params }) {
  const post = await fetchPost(params.slug);
  return { props: { post } };
}

export default function BlogPost({ post }) {
  return <article>{post.content}</article>;
}
App Router — fetch lives in the component·tsx
// app/blog/[slug]/page.tsx
export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params; // v15+: params is a Promise
  const post = await fetchPost(slug);
  return <article>{post.content}</article>;
}

External links

Exercise

Take any Pages Router route you've written (or the official sample) and rewrite it as an App Router route. Show both versions side by side and call out which behaviors became free (loading, error, streaming) and which moved (data fetching).

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.