Routing is a fundamental concept in modern web development, and few frameworks make it as intuitive and powerful as Next.js. Unlike traditional React applications that rely on third-party libraries, Next.js provides a built-in, file-based routing system that simplifies navigation and improves developer productivity.
This guide explores how routing works in Next.js, covering both the modern App Router and the legacy Pages Router, with practical examples and detailed comments.
Understanding File-Based Routing
In Next.js, your folder structure directly maps to URLs. This means:
/app/about/page.js → /about
Let’s see a simple example:
// File: app/page.js
// This is the homepage ("/")
export default function HomePage() {
return (
<div>
{/* Main heading */}
<h1>Welcome to Home Page</h1>
{/* Simple paragraph */}
<p>This route is automatically mapped from app/page.js</p>
</div>
)
}
The App Router: The Modern Standard
The App Router lives inside the /app directory and is now the recommended approach.
Basic Routes
// File: app/about/page.js
// This creates the "/about" route automatically
export default function AboutPage() {
return (
<div>
<h1>About Page</h1>
{/* Explaining routing */}
<p>This page is created using file-based routing.</p>
</div>
)
}
Nested Routing
// File: app/dashboard/settings/page.js
// URL: /dashboard/settings
export default function SettingsPage() {
return (
<div>
<h1>Settings</h1>
{/* This page is nested inside dashboard */}
<p>This is a nested route inside dashboard.</p>
</div>
)
}
Dynamic Routes
Dynamic routes allow us to create reusable templates.
// File: app/blog/[slug]/page.js
// Example URL: /blog/hello-world
export default function BlogPost({ params }) {
// params.slug contains the dynamic part of the URL
const { slug } = params
return (
<div>
<h1>Blog Post</h1>
{/* Display dynamic slug */}
<p>Slug: {slug}</p>
{/* In real apps, you would fetch data based on slug */}
</div>
)
}
Catch-All Routes
// File: app/docs/[...slug]/page.js
// Matches multiple URL segments like /docs/a/b/c
export default function DocsPage({ params }) {
const { slug } = params
return (
<div>
<h1>Docs Page</h1>
{/* slug is an array here */}
<p>Path: {slug.join(" / ")}</p>
</div>
)
}
Layouts (Shared UI)
// File: app/layout.js
// This wraps ALL pages in the app
export default function RootLayout({ children }) {
return (
<html>
<body>
{/* Global navigation */}
<nav>
<a href="/">Home</a> | <a href="/about">About</a>
</nav>
{/* Render page content here */}
{children}
{/* Footer */}
<footer>
<p>My App Footer</p>
</footer>
</body>
</html>
)
}
Nested Layout Example
// File: app/dashboard/layout.js
// This layout ONLY applies to /dashboard routes
export default function DashboardLayout({ children }) {
return (
<div>
<h2>Dashboard Layout</h2>
{/* Sidebar */}
<aside>Sidebar Menu</aside>
{/* Main content */}
<main>{children}</main>
</div>
)
}
Loading and Error UI
// File: app/loading.js
// Shows while page is loading
export default function Loading() {
return <p>Loading...</p>
}
// File: app/error.js
// Handles runtime errors
"use client" // required for error boundaries
export default function Error({ error, reset }) {
return (
<div>
<h2>Something went wrong!</h2>
{/* Show error message */}
<p>{error.message}</p>
{/* Retry button */}
<button onClick={() => reset()}>
Try Again
</button>
</div>
)
}
Navigation in Next.js
Using Link Component
// File: app/page.js
import Link from "next/link"
export default function HomePage() {
return (
<div>
<h1>Home</h1>
{/* Client-side navigation (no page reload) */}
<Link href="/about">Go to About</Link>
</div>
)
}
Programmatic Navigation
// Example using router
"use client"
import { useRouter } from "next/navigation"
export default function GoToPage() {
const router = useRouter()
function handleClick() {
// Navigate to /about when button is clicked
router.push("/about")
}
return (
<button onClick={handleClick}>
Go to About
</button>
)
}
Pages Router (Legacy)
Basic Example
// File: pages/index.js
export default function Home() {
return <h1>Home Page</h1>
}
Dynamic Route Example
// File: pages/blog/[slug].js
import { useRouter } from "next/router"
export default function Blog() {
const router = useRouter()
// Extract slug from URL
const { slug } = router.query
return (
<div>
<h1>Blog Page</h1>
<p>Slug: {slug}</p>
</div>
)
}
API Route Example
// File: pages/api/hello.js
export default function handler(req, res) {
// Handle GET request
if (req.method === "GET") {
return res.status(200).json({
message: "Hello from API"
})
}
// Handle unsupported methods
res.status(405).json({
error: "Method not allowed"
})
}
Best Practices
Use the App Router for new projects
Keep routes organized using folders
Use dynamic routes for reusable pages
Avoid duplicating layouts
Handle loading and error states properly
Conclusion
Routing in Next.js is designed to be simple yet powerful. With features like file-based routing, dynamic segments, layouts, and API routes, developers can build scalable applications efficiently.
The App Router represents the future of Next.js development, offering better structure, improved performance, and a more intuitive developer experience.
By mastering these routing concepts and practicing with real examples, you can build modern web applications that are both maintainable and performant.
Comments
Post a Comment