Skip to content

Pages Router

The Pages Router is the original Next.js routing system, built around the pages/ directory. Even though the App Router is now recommended, huge numbers of production codebases still run on the Pages Router, so this lesson covers its conventions.

What Is the Pages Router?

In the Pages Router, every file inside the pages/ directory automatically becomes a route based on its file path — pages/about.tsx becomes /about, and pages/index.tsx becomes /. Unlike the App Router, components in pages/ are Client Components in spirit: they render on request (or at build time) but are not React Server Components, and data fetching happens through special exported functions rather than async components.

Because the Pages Router predates React Server Components, its data-fetching model (getStaticProps, getServerSideProps, getStaticPaths) is function-based: you export a function alongside your page component, and Next.js calls it before rendering, passing the result in as props.

// pages/posts/[id].tsx
export async function getServerSideProps({ params }: { params: { id: string } }) {
  const post = await fetch(`https://api.example.com/posts/${params.id}`).then((r) => r.json());
  return { props: { post } };
}

export default function PostPage({ post }: { post: any }) {
  return <h1>{post.title}</h1>;
}

getServerSideProps runs on every request on the server, and its return value is passed straight into the page component as props.

Pages Router File Conventions

pages/
  _app.tsx        // Wraps every page (like a root layout)
  _document.tsx   // Customizes the <html> document shell
  index.tsx       // Route: /
  about.tsx       // Route: /about
  posts/
    [id].tsx      // Route: /posts/:id
  api/
    hello.ts      // API route: /api/hello
  • _app.tsx wraps every page, similar to the App Router's root layout.tsx.
  • _document.tsx controls the top-level HTML document, including <html> and <head> structure.
  • Bracketed filenames like [id].tsx define dynamic route segments.
  • Files under pages/api/ automatically become serverless API endpoints.

Pages Router Cheat Sheet

The data-fetching functions and files that define Pages Router behavior.

API / File Purpose
getStaticProps Fetch data at build time (SSG)
getStaticPaths Declare which dynamic routes to pre-render
getServerSideProps Fetch data on every request (SSR)
_app.tsx Global wrapper for every page
_document.tsx Customize the HTML document shell
pages/api/*.ts Serverless API routes
useRouter() Access routing info from next/router

The Three Data-Fetching Functions

Pages Router pages choose their rendering strategy by exporting one specific function. Exporting nothing means the page is statically rendered at build time with no data; exporting getStaticProps fetches data at build time; exporting getServerSideProps fetches data on every request.

Function When It Runs Rendering Result
(none) Build time only Fully static HTML
getStaticProps Build time (or on revalidation) Static HTML with data (SSG/ISR)
getServerSideProps Every request Fresh HTML per request (SSR)

Can pages/ and app/ Coexist?

Yes — Next.js allows pages/ and app/ to exist in the same project simultaneously, which is exactly how most teams migrate incrementally: new routes are added under app/, while existing pages/ routes keep working unchanged until they're eventually ported over.

If the same route exists in both directories, the App Router takes priority, so plan migrations route-by-route rather than duplicating a path in both places.

Common Mistakes

  • Trying to use App Router-only APIs like Server Actions or "use client" inside pages/.
  • Forgetting that getStaticPaths is required whenever a dynamic route ([id].tsx) uses getStaticProps.
  • Assuming getServerSideProps and getStaticProps can both be exported from the same page — only one is allowed.
  • Duplicating the same route path in both pages/ and app/, causing unexpected routing conflicts.

Key Takeaways

  • The Pages Router uses the pages/ directory, where file paths map directly to routes.
  • Data fetching uses exported functions: getStaticProps, getStaticPaths, and getServerSideProps.
  • _app.tsx and _document.tsx play roles similar to the App Router's root layout.
  • pages/api/*.ts files become serverless API endpoints, similar to Route Handlers.
  • pages/ and app/ can coexist during an incremental migration to the App Router.

Pro Tip

If you're maintaining an older Next.js codebase, don't feel pressured to rewrite everything to the App Router at once — Next.js explicitly supports incremental migration, so move high-traffic or actively-developed routes first and leave stable pages on the Pages Router until there's a real reason to touch them.