React  

Routing in Next.js with the Pages Router

Introduction

Next.js has its own way of handling pages and routing, and honestly, it’s one of the nicest parts of the framework. We don’t need to manually set up routes. The file structure is the router.

In a regular React app, we don’t really have a concept of pages. You’ve probably used React-Router to simulate that, right? In Next.js, pages are part of the framework itself. It’s built on file-based routing, which means the structure of your /pages directory actually defines your app’s routes.

No need to manually set up routes. Just drop in a .js or .tsx file in /pages, and now it’s a route.

How Do Pages Work in Next.js?

We create a file inside the /pages folder, and Next.js automatically turns that into a route.

Let me show you what I mean,

/pages
  index.js         // route: "/"
  about.js         // route: "/about"
  contact.js       // route: "/contact"
  blog/
    index.js       // route: "/blog"
    [id].js      // dynamic route: "/blog/routing-nextjs"

Each of those files just exports a React component. Like this.

// pages/about.js
export default function About() {
    return <h1>About Us</h1>
}

That’s literally all you need for a working page. No router setup, nothing else. Just the file.

Static Pages (The Basics)

Let’s say we have a simple about.js page.

// pages/about.js
export default function About() {
  return (
    <div>
      <h1>About Us</h1>
      <p>This is a static page. No data fetching or anything is happening here.</p>
    </div>
  )
}

We hit /about in the browser, and we'll see this. No need to import a router or register the route anywhere. It just works.

Linking Between Pages

For internal linking pages, here we don’t use plain <a> tags but the Link component from the next/link module. Because we don't want the whole page to reload when we click a link, and next/link enables client-side navigation. It means our app behaves like a single-page app(SPA), and it will have fast transitions without full reloads.

// pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <div>
      <h1>Welcome</h1>
      <Link href="/about">Go to About</Link>
    </div>
  )
}

Here, clicking 'Go to About' will navigate without a full page reload, thanks to Next.js’s client-side routing.

Dynamic Routes

Static files are great for fixed pages like /about, but for dynamic content like blog posts, where the route depends on the post, we can't always keep creating pages to create different routes like /blog/hello-nextjs or /blog/react-hooks. So, we use dynamic routing with brackets like [id].js.

export default function Post({ postData }) {
  return <h1>Post Title: {postData.title}</h1>
}

export async function getStaticPaths() {
  const paths = [
    { params: { id: 'hello-nextjs' } },
    { params: { id: 'react-hooks' } },
  ];
  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const postData = await fetchPostById(params.id);
  return { props: { postData } };
}

Here, we render a blog post title using static generation. getStaticPaths defines which dynamic routes (based on post IDs) to pre-render. getStaticProps fetches the post data at build time for each route using fetchPostById.

Note. Pages must export a component by default, or else Next.js will throw an error.

Nested Routes

We can nest folders to create nested routes. However, we can’t nest routes infinitely unless we create corresponding folders for them. This approach keeps our code organized without requiring additional routing logic.

For example: /pages/blog/posts/what-is-nextjs.js becomes /blog/posts/what-is-nextjs

Conclusion

Next.js Pages are simple but powerful. The core idea is that our folder structure is our routing system. That means less boilerplate, fewer things to configure, and a faster setup, especially for teams.

Next.js basically gives us a full-featured React app with routing, server-side rendering, and even API support. We just need to follow its conventions and we're good to go.