Introduction
So, in the last article, we used the /pages directory in Next.js for routing, and that’s the classic way. But now, Next.js has introduced a brand new routing system called the App Router. It’s super powerful, and once you get the hang of it, it feels kind of easier.
What Is the App Router?
The App Router enables route definition through folder structure within the /app
directory, mapping folders to URL paths. This approach supports nested layouts, group routes, and simplifies data fetching in larger applications for better route management.
This newer system gives us:
- Layouts (shared UI across pages)
- Nested routing
- Server and client components
- Loading states
- Error handling
- Better control over rendering (SSR, SSG, etc.)
It’s more flexible than the old way.
Create a new Next.js project using the npx create-next-app@latest command. In Next.js 14, the /app directory is set up by default, and this enables the App Router. No extra configuration is needed.
Static Routes
The App Router still uses file-based routing, but the files look a bit different. We don’t write our page directly as about.js anymore; now we use folders and a page.js file inside each route.
In your project, create folders and files like those shown above. If you are using JavaScript, the page name should be page.js, and if you are using TypeScript, it would be page.tsx. Everything else is the same.
// Folder structure
/app
page.js // route - /
about/
page.js // route- /about
contact/
page.js // route - /contact
Every route is just a folder now, and each folder must have a page.js (or page.tsx in case of TypeScript) file for it to be a routable page.
In app/about/page.js
// app/about/page.js
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>This is a static page using the App Router.</p>
</div>
);
}
We just hit /about in the browser, and that’s our page. No need to set up routing manually. Next.js handles it based on the folder and file structure.
Dynamic Routes
Dynamic routes still use square brackets like before, but now they’re folders too.
For a dynamic route, our folder structure will look like below -
/app
blog/
[id]/
page.js // route - /blog/what-is-nextjs
And inside that page.js, we can access route parameters like this-
export default function Post({ params }) {
return <h1>Blog Post: {params.id}</h1>
}
Layouts
Layouts in App Router are good. We can create a layout.js file at any level to wrap all child pages. Think navbars, sidebars, or persistent headers now no longer duplicate UI everywhere.
Example
/app
layout.js // wraps all routes
dashboard/
layout.js // wraps all dashboard pages
page.js // /dashboard
settings/
page.js // /dashboard/settings
Each nested layout wraps everything beneath it. It's like nesting layouts inside layouts, and it's super clean.
Here’s a simple layout file-
// app/layout.js
export default function RootLayout({ children }) {
return (
<html>
<body>
<header>My App</header>
<main>{children}</main>
</body>
</html>
)
}
Here, this is a Next.js layout component (app/layout.js) that wraps all pages. It returns an HTML structure with a <header> and <main> tag containing the page content ({children}). This layout is shared across all routes in the Next.js app directory.
Error Handling
We can add an error.js file inside any route folder to catch errors for that route.
/app/dashboard/
page.js
error.js // shows if dashboard fails to load
We do not need to wrap everything in try/catch anymore.
Loading UI
If we want to show a spinner while a page is loading, we can just add a loading.js file next to our page.js.
/app/blog/[id]/
page.js
loading.js
In loading.js
export default function Loading() {
return <p>Loading blog post...</p>;
}
Next.js will automatically show this while the route is loading. We do not need to handle it manually.
Linking Between Routes
It's the same as usual, we use the Link component from next/link -
import Link from 'next/link'
<Link href="/about">About</Link>
This works the same in both routing systems.
Route Groups
Now this one’s super handy. Sometimes, we want to organize our routes into folders, maybe for separating marketing pages vs app pages, but we don’t want that folder name to show up in the URL.
That’s where Route Groups come in. Just wrap the folder name in parentheses-
/app
(marketing)/ // this will be ignored
about/
page.js // still becomes "/about"
The (marketing) folder is ignored in the final route path. This is useful for grouping routes logically (e.g., marketing vs. dashboard) without changing the URL structure.
It’s especially useful when we want clean routes like /login or /register, but still want to keep them grouped under something like (auth) internally.
Pages Router vs App Router
Here is a side-by-side comparison table for the page router and app router -
Feature |
Pages Router |
App Router |
File-based routing |
Yes |
Yes |
Layout support |
Manual |
Built-in |
Loading/error components |
Manual or third party |
Built-in |
Server Components |
No |
Yes (default) |
Route groups |
Not supported |
Yes |
Recommended by Next.js |
Legacy (Still works) |
Future-proof |
If you’re starting a new project, go with the App Router; it’s the modern, more powerful way. If you're working with an older app or doing a quick prototype, the Pages Router might still feel quicker and simpler.
Conclusion
The App Router might seem like a lot at first (layouts, folders, loading states, etc.), but once you start using it, it actually simplifies building large-scale applications in the real world. Everything just feels more organized. Routing, layouts, and even data fetching are built right into the structure.
So if you're just getting started, don't worry about mastering it all at once. Try building a small project using /app, and you will get used to it.