Next.js supports file-based routing, meaning that the folder structure inside the pages/
or app/
directory automatically defines routes. Let’s explore both App Router (Next.js 13+) and Pages Router (Legacy).
app/
)Introduced in Next.js 13, the App Router (app/
directory) uses React Server Components (RSC) and a more modern routing system.
.js
, .jsx
, or .tsx
file inside app/
becomes a route.app/├── layout.js → Shared layout (wrapping all pages)├── page.js → Home page ("/")├── about/│ ├── page.js → About page ("/about")│ ├── team/│ ├── page.js → Team page ("/about/team")├── blog/│ ├── page.js → Blog homepage ("/blog")│ ├── [slug]/│ ├── page.js → Dynamic route ("/blog/:slug")
/
)app/page.js
export default function HomePage() {return <h1>Welcome to Next.js</h1>;}
/about
)app/about/page.js
export default function AboutPage() {return <h1>About Us</h1>;}
[slug]
)For dynamic pages, use square brackets [param]
in folder names.
Example for /blog/:slug
:
app/blog/├── [slug]/│ ├── page.js → "/blog/:slug"
app/blog/[slug]/page.js
export default function BlogPost({ params }) {return <h1>Blog Post: {params.slug}</h1>;}
📌 URL /blog/nextjs-routing
→ Shows Blog Post: nextjs-routing
[...slug]
)For handling multiple segments dynamically (/docs/a/b/c
), use [...slug]
.
Example:
app/docs/├── [...slug]/│ ├── page.js → Handles "/docs/a", "/docs/a/b", etc.
app/docs/[...slug]/page.js
export default function Docs({ params }) {return <h1>Docs for {params.slug?.join(" / ")}</h1>;}
📌 /docs/nextjs/routing
→ Shows Docs for nextjs / routing
layout.js
)layout.js
allows shared layouts across multiple pages.
Example: app/layout.js
export default function RootLayout({ children }) {return (<html lang="en"><body><nav>Navbar</nav>{children}</body></html>);}
📌 Now all pages (/
, /about
, /blog
) share this layout.
pages/
)The Pages Router (legacy approach) uses the pages/
directory for routing.
pages/├── index.js → Home page ("/")├── about.js → About page ("/about")├── blog/│ ├── index.js → Blog homepage ("/blog")│ ├── [slug].js → Dynamic route ("/blog/:slug")
Example for /about
:
pages/about.js
export default function AboutPage() {return <h1>About Us</h1>;}
[slug].js
)pages/blog/├── [slug].js → "/blog/:slug"
pages/blog/[slug].js
import { useRouter } from "next/router";export default function BlogPost() {const router = useRouter();return <h1>Blog Post: {router.query.slug}</h1>;}
📌 /blog/nextjs-routing
→ Blog Post: nextjs-routing
pages/api/
)API endpoints live in pages/api/
.
Example: pages/api/hello.js
export default function handler(req, res) {res.status(200).json({ message: "Hello from API!" });}
📌 Access at /api/hello
Feature | App Router (app/ ) | Pages Router (pages/ ) |
---|---|---|
React Components | Server & Client | Client only |
Layouts | layout.js (built-in) | Manual _app.js |
Dynamic Routes | app/[slug]/page.js | pages/[slug].js |
API Routes | Uses Route Handlers (app/api/route.js ) | pages/api/*.js |
Performance | Faster (RSC, no hydration) | Slower (hydration needed) |
📌 New projects should use the App Router (app/
) for better performance and scalability.
The App Router allows deeply nested layouts.
Example:
app/├── layout.js → Global Layout├── dashboard/│ ├── layout.js → Dashboard Layout│ ├── page.js → "/dashboard"│ ├── settings/│ ├── page.js → "/dashboard/settings"
app/dashboard/layout.js
export default function DashboardLayout({ children }) {return (<div><nav>Dashboard Menu</nav>{children}</div>);}
📌 Now /dashboard
and /dashboard/settings
share this layout.
Next.js App Router allows Route Groups to organize routes without affecting the URL structure. This is useful for:
✅ Organizing files without changing paths
✅ Creating nested layouts for specific sections
✅ Grouping routes for middleware, authentication, or localization
To create a grouped route, wrap it inside (parenthesis):
app/├── (dashboard)/ → Route Group (Does not appear in URL)│ ├── layout.js → Shared layout for dashboard pages│ ├── page.js → "/"│ ├── analytics/│ │ ├── page.js → "/analytics"│ ├── settings/│ ├── page.js → "/settings"
📌 Even though dashboard/
exists in the folder structure, it won’t appear in the URL.
Create app/(dashboard)/layout.js
:
export default function DashboardLayout({ children }) {return (<div><nav>Dashboard Navigation</nav>{children}</div>);}
📌 Now all pages in (dashboard)/
share this layout.
app/(dashboard)/page.js
export default function DashboardHome() {return <h1>Welcome to the Dashboard</h1>;}
📌 Accessible at /
, NOT /dashboard
app/(dashboard)/analytics/page.js
export default function AnalyticsPage() {return <h1>Analytics Data</h1>;}
📌 Accessible at /analytics
, NOT /dashboard/analytics
app/(dashboard)/settings/page.js
export default function SettingsPage() {return <h1>Settings Page</h1>;}
📌 Accessible at /settings
, NOT /dashboard/settings
✅ Better Organization – Keep related pages together without affecting URLs
✅ Shared Layouts – Apply layout.js
to only part of the app
✅ Middleware & Authentication – Apply Next.js Middleware to a specific group
You can protect a route group using middleware (middleware.js
in the root):
import { NextResponse } from "next/server";export function middleware(req) {const isLoggedIn = req.cookies.get("auth_token"); // Example auth checkif (!isLoggedIn && req.nextUrl.pathname.startsWith("/dashboard")) {return NextResponse.redirect(new URL("/login", req.url));}}
📌 Now all /dashboard
routes require authentication.
You can use route groups for internationalization:
app/├── (en)/│ ├── page.js → "/"│ ├── about/│ ├── page.js → "/about"├── (fr)/│ ├── page.js → "/fr"│ ├── about/│ ├── page.js → "/fr/about"
📌 This creates localized URLs (/
and /fr/
), keeping code organized.
✔ Use (group)/
to organize pages without affecting URLs
✔ Apply shared layouts inside a group
✔ Combine with middleware for authentication or localization
✔ Works great with dynamic routes & API routes
✔ Use app/
(App Router) for modern Next.js projects.
✔ File structure = route structure (easy to manage).
✔ Leverage layouts for shared UI.
✔ Use [slug]
and [...slug]
for dynamic & catch-all routes.
Quick Links
Legal Stuff
Social Media