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->/aboutapp/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->/aboutapp/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.