next.js
iprudhvi.in

Routing in Next.js (App Router) - A Complete Guide (2025)

A guide to routing in Next.js (App Router) covering Catch-All Segments, Dynamic Routes, Nested Routes, and more.

March 12, 2025
Next.js

Routing Doesn't Have to Be Hard

Let's talk about Next.js routing. Specifically, the App Router.

When the App Router first dropped, everyone lost their minds. It felt like we had to re-learn everything we knew about building React apps. Folders inside of folders, page.tsx, layout.tsx, loading.tsx... it felt like over-engineering at its finest.

But after using it in production for a while, I’ll admit it: it’s actually pretty brilliant.

Let me explain routing to you like we're grabbing a coffee. No academic jargon. Just how it actually works and where you're going to screw it up.

The Folder is the Route

In the old pages router, the file name was the route. about.js became /about.

In the App Router, the folder is the route, and the page.tsx file inside it is the UI.

  • app/page.tsx -> /
  • app/about/page.tsx -> /about
  • app/dashboard/settings/page.tsx -> /dashboard/settings

It’s that simple. If there is no page.tsx in a folder, that folder is not a public route. It’s just a folder for organizing your code.

This is a massive upgrade. You can finally put your components, styles, and utils right next to the page that uses them.

Dynamic Routes: The Bracket Syntax

What if you have a blog and you need a route for each post? You can't create a folder for every single blog post.

You use dynamic segments. Wrap the folder name in brackets.

app/blog/[slug]/page.tsx

Now, if a user goes to /blog/hello-world, Next.js will render that page and pass hello-world as the slug parameter.

export default function BlogPost({ params }) {
  return <h1>Post Slug: {params.slug}</h1>
}

The Gotcha: You have to await the params in newer versions of Next.js if you want to use them in server components. It's an async operation now.

Catch-All Segments: The Black Hole

Sometimes you don't know how deep a URL will go. Maybe you're building a documentation site: /docs/getting-started/installation/mac.

You don't want to create folders for every possible depth. You use a catch-all route.

Add three dots inside the brackets: app/docs/[...slug]/page.tsx.

Now, that single page will catch /docs/a, /docs/a/b, /docs/a/b/c, and so on. The params.slug will be an array of the URL segments.

The Real World Use Case: I use this all the time for rendering Markdown content from a CMS where the folder structure is dynamic.

Layouts: The Unsung Heroes

This is where the App Router shines.

In a traditional React app, maintaining state (like a sidebar or a media player) across page navigations is annoying. The whole page re-renders.

In the App Router, you use layout.tsx.

A layout wraps the page.tsx (and any nested layouts). When you navigate between pages that share the same layout, the layout does not re-render.

// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
  return (
    <div className="flex">
      <Sidebar />
      <main>{children}</main>
    </div>
  )
}

Put your heavy, static stuff in the layout. Put the dynamic, changing stuff in the page. Your app will feel instantly faster.

Route Groups: Organizing the Chaos

As your app grows, your app directory will become a messy list of folders.

What if you want to group all your marketing pages together, and all your app pages together, without changing the URL?

Use a Route Group. Wrap a folder name in parentheses.

app/(marketing)/about/page.tsx still resolves to /about. The (marketing) folder is ignored in the URL path.

The Pro Tip: You can put a layout.tsx inside a Route Group. This lets you have completely different layouts for your marketing site and your logged-in dashboard, without messy conditional logic.

The Silent Killer: Link Components

Here is a mistake I see every day.

Developers use standard <a> tags for internal navigation.

<!-- WRONG -->
<a href="/dashboard">Go to Dashboard</a>

If you do this, the browser will do a full page reload. You lose all your React state. You lose the performance benefits of Next.js.

Always, always, always use the Next.js <Link> component.

import Link from 'next/link'

// RIGHT
<Link href="/dashboard">Go to Dashboard</Link>

It pre-fetches the page in the background when the user hovers over the link. The navigation is instantaneous.

The Reality Check

The App Router is powerful, but it has a steep learning curve. You are going to get weird caching errors. You are going to accidentally use a server feature in a client component.

Don't panic. Read the error messages.

Start simple. Don't try to use parallel routes and intercepting routes on day one. Master folders, pages, and layouts first.

— Build a mental model of the folder structure, and the rest will follow.

Routing Doesn't Have to Be Hard

Let's talk about Next.js routing. Specifically, the App Router.

When the App Router first dropped, everyone lost their minds. It felt like we had to re-learn everything we knew about building React apps. Folders inside of folders, page.tsx, layout.tsx, loading.tsx... it felt like over-engineering at its finest.

But after using it in production for a while, I’ll admit it: it’s actually pretty brilliant.

Let me explain routing to you like we're grabbing a coffee. No academic jargon. Just how it actually works and where you're going to screw it up.

The Folder is the Route

In the old pages router, the file name was the route. about.js became /about.

In the App Router, the folder is the route, and the page.tsx file inside it is the UI.

  • app/page.tsx -> /
  • app/about/page.tsx -> /about
  • app/dashboard/settings/page.tsx -> /dashboard/settings

It’s that simple. If there is no page.tsx in a folder, that folder is not a public route. It’s just a folder for organizing your code.

This is a massive upgrade. You can finally put your components, styles, and utils right next to the page that uses them.

Dynamic Routes: The Bracket Syntax

What if you have a blog and you need a route for each post? You can't create a folder for every single blog post.

You use dynamic segments. Wrap the folder name in brackets.

app/blog/[slug]/page.tsx

Now, if a user goes to /blog/hello-world, Next.js will render that page and pass hello-world as the slug parameter.

export default function BlogPost({ params }) {
  return <h1>Post Slug: {params.slug}</h1>
}

The Gotcha: You have to await the params in newer versions of Next.js if you want to use them in server components. It's an async operation now.

Catch-All Segments: The Black Hole

Sometimes you don't know how deep a URL will go. Maybe you're building a documentation site: /docs/getting-started/installation/mac.

You don't want to create folders for every possible depth. You use a catch-all route.

Add three dots inside the brackets: app/docs/[...slug]/page.tsx.

Now, that single page will catch /docs/a, /docs/a/b, /docs/a/b/c, and so on. The params.slug will be an array of the URL segments.

The Real World Use Case: I use this all the time for rendering Markdown content from a CMS where the folder structure is dynamic.

Layouts: The Unsung Heroes

This is where the App Router shines.

In a traditional React app, maintaining state (like a sidebar or a media player) across page navigations is annoying. The whole page re-renders.

In the App Router, you use layout.tsx.

A layout wraps the page.tsx (and any nested layouts). When you navigate between pages that share the same layout, the layout does not re-render.

// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
  return (
    <div className="flex">
      <Sidebar />
      <main>{children}</main>
    </div>
  )
}

Put your heavy, static stuff in the layout. Put the dynamic, changing stuff in the page. Your app will feel instantly faster.

Route Groups: Organizing the Chaos

As your app grows, your app directory will become a messy list of folders.

What if you want to group all your marketing pages together, and all your app pages together, without changing the URL?

Use a Route Group. Wrap a folder name in parentheses.

app/(marketing)/about/page.tsx still resolves to /about. The (marketing) folder is ignored in the URL path.

The Pro Tip: You can put a layout.tsx inside a Route Group. This lets you have completely different layouts for your marketing site and your logged-in dashboard, without messy conditional logic.

The Silent Killer: Link Components

Here is a mistake I see every day.

Developers use standard <a> tags for internal navigation.

<!-- WRONG -->
<a href="/dashboard">Go to Dashboard</a>

If you do this, the browser will do a full page reload. You lose all your React state. You lose the performance benefits of Next.js.

Always, always, always use the Next.js <Link> component.

import Link from 'next/link'

// RIGHT
<Link href="/dashboard">Go to Dashboard</Link>

It pre-fetches the page in the background when the user hovers over the link. The navigation is instantaneous.

The Reality Check

The App Router is powerful, but it has a steep learning curve. You are going to get weird caching errors. You are going to accidentally use a server feature in a client component.

Don't panic. Read the error messages.

Start simple. Don't try to use parallel routes and intercepting routes on day one. Master folders, pages, and layouts first.

— Build a mental model of the folder structure, and the rest will follow.

Comments

Related Posts

The Clarity Debt

Why knowing more has made us decide less.

July 22, 2026
Read more

Next JS Data Fetching mistakes & Security vulnerabilities

A guide to Next.js data fetching mistakes & security vulnerabilities

July 5, 2025
Read more

JavaScript for Frontend Development: A Beginner's Guide

A guide to JavaScript for frontend development for beginners part 1

April 13, 2025
Read more