Routing in Next.js

Grasp the fundamentals of routing in Next.js and implement it effectively in your projects. Create user-friendly URLs that reflect your application’s structure, manage dynamic content based on URL segments, and navigate users between pages programmatically using Next.js’s convenient features. This ensures a smooth and intuitive user experience as they explore your web application.

Routing-in-Nextjs
Table of Contents

Routing in Next.js

Routing is a fundamental concept in web development that defines how users navigate between different sections of your application. In Next.js, routing is crucial in directing users to the appropriate content based on the URL they access.

Core Principles

  • File-based routing: Next.js utilizes a file-system-based approach for defining routes.
  • Pages directory: The pages directory is the central location for your application’s routes.
  • Components and Routes: Each JavaScript file within pages represents a route and acts as a React component that renders the corresponding content.

Folder Structure

├── pages/
│   ├── index.js  
│   ├── about.js
pages/  (root directory)
pages/index.js  (home page)
pages/about.js  (about us page)

Explanation

  • Line 1: Represents the root pages directory.
  • Lines 2-3: Files within pages define routes.
    • / (home page) maps to pages/index.js.
    • /about (about us page) maps to pages/about.js.

Using Next.js routing effectively, you can create well-organized and user-friendly navigation experiences within your web applications.


Defining Routes in Next.js

Next.js supports a file-based routing system, making it intuitive to define static, dynamic, and nested routes for your application. Here’s how the process works:

1. The pages Directory

The pages directory is the core of routing in Next.js. Each JavaScript file within this directory represents a route in your application.

2. Static Routes

Create a JavaScript file directly within the pages directory (e.g., about.js, contact.js). The file’s name (excluding the .js extension) will become the route path in the application (e.g., /about, /contact).

3. Dynamic Routes

Introduce dynamic segments in your routes using square brackets [ ] in the filename. (e.g., [postId].js to fetch blog posts with different post IDs). This file becomes a route that can match multiple URLs, such as /posts/1, /posts/2, etc.

4. Nested Routes

Create directories within your pages directory to establish a nested route structure. For example, a directory named products with a [productId].js file within will create routes like /products/123.

Folder Structure, Demonstrating How to Organize the Routes Defined by Your JavaScript Files:

├── pages/
│   ├── index.js  
│   ├── contact.js
│   ├── blog/
│   │   └── [postId].js 
│   └── products/
│       ├── [categoryId]/
│       │   ├── index.js
│       │   └── [productId].js 
pages/index.js (represents the homepage '/')
pages/contact.js
pages/blog/[postId].js 
pages/products/[categoryId]/index.js 
pages/products/[categoryId]/[productId].js

Explanation

  • Line 1: Represents your website’s homepage, accessible at the root URL (/).
  • Line 2: Static route for the contact page (e.g., /contact).
  • Line 3: Dynamic route representing individual blog posts. The [postId] segment indicates that different URLs based on the postId (e.g., /blog/123, /blog/456) will map to this component.
  • Line 4: Represents a product category page. The [categoryId] dynamic segment allows for different category pages (e.g., /products/electronics, /products/clothing).
  • Line 5: A dynamic route for individual product detail pages nested under product categories. The [categoryId] and [productId] segments ensure routing to specific products within categories (e.g., /products/electronics/abc).

Key Points

  • File System-based Routing: Next.js uses the directory structure within pages to determine routes.
  • Static Routing: Files like index.js and contact.js define static routes with a fixed URL structure.
  • Dynamic Routing: Square brackets [ ] define dynamic route segments, allowing you to render varying content based on URL parameters.
  • Nested Routing: Subfolders within the pages directory create nested routes for a well-organized URL hierarchy.

Creating Pages and Layouts in Next.js

Next.js offers a powerful way to structure your application’s user interface (UI) with pages and layouts. Layouts allow you to create reusable UI components like headers, footers, and navigation menus that appear consistently across multiple pages.

Here’s How to Create Pages and Layouts in next.js With a Navigation Link:

1. Project Setup

Ensure you have a Next.js project set up. You can use tools like create-next-app to initialize a new project.

2. Components for Header and Footer

Create separate React components, for example, Header.js and Footer.js, to define the content and styles for your header and footer.

3. Layout Component

Create a Layout.js component that will be a wrapper for your page content. This component will render the header, footer, a placeholder for the actual page content, and a navigation link.

4. Pages with Layout Integration

Create individual page components like About.js for your “About Us” page. Inside these page components, you’ll render the layout component and pass the specific content for that page.

Project Structure

my-next-project/
├── pages/
│   ├── index.js  
│   ├── about.js  
│   └── layouts.js  
├── components/
│   ├── Header.js
│   └── Footer.js 
├── public/ 
│   ├── logo.png  
│   └── favicon.ico
├── next.config.js 
└── package.json

Explanation

  • pages/
    • index.js: Represents the home page (/) of your application.
    • about.js: Represents the “About Us” page (/about).
  • components/
    • Header.js: Contains the code for your header, including the website title and navigation links.
    • Footer.js: Contains the code for your footer, such as copyright information and other links.
  • public/
    • A directory to store static assets such as images (logo.png) and your favicon.
  • next.config.js: Optional file for Next.js specific configurations.
  • package.json: Contains project dependencies and scripts.

components/Header.js

import Link from 'next/link';

export default function Header() {
    return (
        <header>
            <h1>My Website</h1>
            <nav>
                <Link href="/">Home</Link>
                <Link href="/about">About Us</Link>
            </nav>
        </header>
    );
}

components/Footer.js

export default function Footer() {
    return (
        <footer>
            <p>Copyright &copy; 2024</p>
        </footer>
    );
}

pages/Layout.js

import Header from './Header';
import Footer from './Footer';
import { Link } from 'next/link';

export default function Layout({ children }) {
    return (
        <div>
            <Header />
            {children}  {/* Placeholder for page content */}
            <Footer />
        </div>
    );
}

pages/About.js

import Layout from './Layout';

export default function About() {
    return (
        <Layout>
            <h1>About Us</h1>
            <p>This is the content for our About Us page.</p>
        </Layout>
    );
}

Explanation

  • Lines 1-13 & 14-20: Define separate components for Header and Footer with their specific content.
  • Lines 21-33: Create the Layout component.
    • Line 21-22: Imports the Header and Footer components.
    • Line 23: Imports the Link component from next/link for navigation.
    • Lines 28: Renders the Header component with navigation links using the Link component.
      • href prop in Link specifies the target route (/ for home, /about for About Us).
    • Line 29: Renders the children prop, which will be filled by the content from individual pages.
    • Lines 30: Renders the Footer component.
  • Lines 34-43: Define the About.js page component (unchanged).

Remember

  • Create separate components for reusable UI elements like headers, footers, and navigation menus.
  • The Layout component acts as a wrapper, providing a consistent structure for your pages, including navigation.
  • Individual pages like About.js import and utilize the Layout component, injecting their specific content.

Static Routing in Next.js

Static routes are the simplest, directly mapped to specific files in the pages directory. This makes creating and managing them quite intuitive. A static route serves content that does not change based on user input or other dynamic parameters. This process involves creating files for each route, where the file path from the pages directory corresponds to the URL path.

Creating Static Routes

  1. Create individual JavaScript files within the pages directory of your Next.js project (e.g., about.js, contact.js).
  2. The filename, excluding the .js extension, becomes the route path for that page (e.g., URLs like /about, /contact).

Project Structure

your-next-project/
├── pages/
│   ├── about.js
│   ├── contact.js
│   └── index.js
├── ... (other folders)
pages/index.js  (represents the homepage '/')
pages/about.js  (represents the '/about' route)
pages/contact.js  (represents the '/contact' route)

Explanation

  • Lines 1-3: Define three static routes:
    • Line 1: Represents the homepage (/) of your application.
    • Line 2: Creates a route accessible at /about.
    • Line 3: Creates a route accessible at /contact.

Example for about.js (Static Route Representing the ‘/about’ Page)

function AboutPage() {
    return (
        <div>
            <h1>About Us</h1>
            <p>Welcome to the About Us page.</p>
        </div>
    );
}

export default AboutPage;

Explanation

  • File Location: The about.js file within the pages directory directly maps to a route in your application.
  • URL Mapping: When a user visits /about in your Next.js application, the AboutPage component is rendered.
  • Static Content: Static routes typically render fixed content that doesn’t change based on dynamic parameters in the URL.

Dynamic Routing in Next.js

Dynamic routing is a powerful feature that allows you to handle routes that match multiple URLs based on variable segments. This is ideal for scenarios where content depends on dynamic data, such as blog posts with unique IDs or product pages with different product categories.

Creating Dynamic Routes

  1. Use square brackets [ ] in filenames within the pages directory (e.g., [postId].js).
  2. This creates a route pattern that can match URLs containing values for the dynamic segment (e.g., /posts/123, /posts/456).

Project Structure

your-next-project/
├── pages/
│   ├── blog/
│   │   └── [postId].js
│   └── ... 
├── ... (other folders)

pages/blog/[postId].js

Example for [postId].js (Dynamic Route for Individual Blog Posts)

import { useRouter } from 'next/router';

function BlogPostPage() {
    const router = useRouter();
    const { postId } = router.query;

    // Fetch blog post data based on the postId (you'd implement this part)
    const postTitle = 'Sample Post Title'; // Placeholder

    return (
        <div>
            <h1>{postTitle}</h1>
            <p>Content of the blog post with ID: {postId}</p>
        </div>
    );
}

export default BlogPostPage;

Explanation

  • Dynamic Segment: Using square brackets [ ] in the filename [postId].js indicates a dynamic route.
  • URL Mapping: This component will handle routes with the following pattern:
    • /blog/123
    • /blog/456
    • … and any other valid ID after the "/blog/" segment.
  • useRouter Hook: The useRouter hook is imported to access the dynamic postId value from the URL.
  • Data Fetching: You would implement logic here to fetch the specific blog post content corresponding to the postId.
  • Rendering Dynamic Content: The content is generated dynamically based on the extracted postId (e.g., post title, body).

Key Points

  • Dynamic routes are perfect for scenarios where you have multiple pages with similar structures but different content (e.g., blog posts, product details, user profiles).
  • Next.js automatically handles extracting the dynamic segment value from the URL path.

Nested Routing in Next.js

Nested routing is a technique to organize your application’s routes hierarchically. This feature is particularly useful for large applications that require a deep level of navigation, such as e-commerce sites, educational platforms, or content-rich portals. By organizing files and folders within the pages directory, developers can define a clear and intuitive URL structure that reflects the content’s hierarchy, making navigation logical and straightforward for the end-user.

Creating Nested Routes

  1. Create nested folders within the pages directory.
  2. Each folder represents a section of your application, and files within those folders define routes within that section.

Project Structure

your-next-project/
├── pages/
│   ├── products/
│   │   ├── index.js  		 	  // Listing of products
│   │   ├── [categoryId].js  		 // Specific product category 
│   │   └── [categoryId]/[productId].js // Individual product details
│   └── ... (other pages)
├── ... (other folders)

products/index.js (Products Listing)

import Link from 'next/link';

function ProductsPage() {
    return (
        <div>
            <h1>Products</h1>
            <ul>
                <li><Link href='/products/electronics'>Electronics</Link></li>
                <li><Link href='/products/clothing'>Clothing</Link></li>
                {/* More product categories */}
            </ul>
        </div>
    );
}

export default ProductsPage;

products/[categoryId].js (Product Category)

import { useRouter } from 'next/router';

function CategoryPage() {
    const router = useRouter();
    const { categoryId } = router.query;

    return (
        <div>
            <h1>{categoryId} Category</h1>
            {/* ... */}
        </div>
    );
}

export default CategoryPage;

products/[categoryId]/[productId].js (Product Details)

import { useRouter } from 'next/router';

function ProductDetailPage() {
    const router = useRouter();
    const { categoryId, productId } = router.query;

    return (
        <div>
            <h1>Product in Category: {categoryId}</h1>
            <p>Product ID: {productId}</p>
            {/* ... */}
        </div>
    );
}

export default ProductDetailPage;

Explanation

  • Nested Structure: Folders within the pages directory create nested routes like:
    • /products
    • /products/electronics
    • /products/electronics/123 (Individual product)
  • Navigation (Link Component): The Link component in products/index.js establishes navigation between nested routes (Lines 8-9).
  • Accessing Parameters: The useRouter hook is used in product category and product detail pages to access relevant route parameters.

Benefits of Nested Routing

  • Organization: Groups related pages under a common folder, promoting a clear project structure.
  • Maintainability: Easier to manage and update code for specific application sections.
  • Improved Navigation: Users can navigate through a hierarchy of routes, mimicking real-world folder structures.

Navigation and Linking in Next.js

Next.js provides a smooth experience for creating navigation elements within your application. Here’s how linking and navigation work:

1. The Link Component

  • Import the Link component from next/link.
  • Use the Link component to wrap anchor tags (<a>) for navigation.
  • Set the desired route path using the href prop on the Link component.

2. Client-Side Navigation

Link enables client-side navigation by default, offering a smoother user experience without full page reloads.

3. Active Link Styling (Optional)

Use the active prop of the Link component to conditionally style the link when it points to the current route.

Example

import Link from 'next/link';

const MyPage = () => {
    return (
        <div>
            <h1>My Page</h1>
            <p>Navigate to other pages:</p>
            <ul>
                <li>
                    <Link href="/about">About Us</Link>
                </li>
                <li>
                    <Link href="/contact">Contact Us</Link>
                </li>
            </ul>
        </div>
    );
};

export default MyPage;

Explanation

  • Line 1: Imports the Link component from next/link.
  • Lines 8-15: Define a navigation list using an unordered list (<ul>) and list items (<li>).
  • Line 10 & 13: Wrap anchor tags (<a>) with the Link component.
    • Line 10: Sets the href prop to /about for the “About Us” link.
    • Line 13: Sets the href prop to /contact for the “Contact Us” link.

Key Points

  • The Link component handles route transitions efficiently.
  • Next.js offers additional routing functionalities like programmatic navigation using the useRouter hook.

Error Handling in Next.js

Error handling is a crucial aspect of any web application, and Next.js provides mechanisms to gracefully manage unexpected errors that might occur during routing and rendering. Here’s a breakdown of the concepts:

1. Client-Side Errors

  • These errors happen during JavaScript execution in the user’s browser.
  • Next.js leverages React’s built-in error boundaries to catch and display user-friendly fallback UI when such errors arise.

2. Server-Side Errors

  • These errors occur during the server-side rendering process of Next.js.
  • Next.js provides a default pages/500.js file to handle these errors and display a custom error page to the user.

Example (pages/500.js)

function ErrorPage({ statusCode }) {
    return (
        <div>
            <h1>Error {statusCode}</h1>
            <p>An unexpected error occurred on this page.</p>
        </div>
    );
}

export default ErrorPage;

Explanation

  • Line 1: Defines a function component named ErrorPage.
  • Line 4: Receives an object prop named statusCode containing the error status code.
  • Lines 3-6: Renders an error message using JSX, displaying the received statusCode.
    • Line 4: Creates an h1 element to display the error code.
    • Line 5: Displays a generic error message.
  • Line 10: Exports the ErrorPage component as the default export.

This example shows a basic custom error page for server-side errors. You can customize the content and styling of this page to provide more informative error messages to your users.


Route Groups in Next.js

Next.js provides a powerful feature called Route Groups that allows you to organize your routes within the pages directory based on specific criteria like application sections, feature areas, or team ownership. This promotes better code maintainability, reusability of layouts, and a clearer project structure.

Using Route Groups

  1. Wrap a folder containing related routes within parentheses ( ).
  2. This folder acts as a group but won’t be included in the final URL path.

Project Structure

your-next-project/
├── pages/ 
│   ├── (account)/  // Route group for account-related pages
│   │   ├── profile.js
│   │   └── settings.js
│   └── ... (other pages)
├── ... (other folders)

account/profile.js

function ProfilePage() {
    return (
        <div>
            <h1>My Profile</h1>
            {/* User profile information */}
        </div>
    );
}

export default ProfilePage;

account/settings.js

function SettingsPage() {
    return (
        <div>
            <h1>Account Settings</h1>
            {/* User account preferences */}
        </div>
    );
}

export default SettingsPage;

Explanation

  • Route Group: The folder (account) acts as a route group. Files within it represent routes under the "/account" segment of the URL.
  • URL Mapping:
    • account/profile.js is accessible at the /account/profile URL.
    • account/settings.js is accessible at the /account/settings URL.
  • Simplified Organization: Route groups promote better code organization by grouping related routes under a common subdirectory.

Key Points

  • Route groups don’t affect the final URL structure in the browser.
  • They are primarily a tool for project organization and potential layout reusability.
  • Layout Reuse (Optional): You can create a layout component within the route group folder to apply a consistent layout or structure to the routes within that group.

Parallel Routes in Next.js

Next.js offers a unique routing concept called Parallel Routes, which allows you to render multiple independent page sections within a single layout. This is particularly useful for applications with complex UIs that require displaying various content areas simultaneously like dashboards with multiple widgets or social media feeds with sidebars.

Using Parallel Routes

  1. Create folders with a leading @ symbol within the pages directory.
  2. These folders represent parallel routes and won’t affect the URL structure.
  3. Each folder can contain its own JavaScript component defining the content for that parallel route.

Project Structure

your-next-project/
├── pages/ 
│   ├── dashboard/  
│   │   ├── index.js  // Main dashboard layout
│   │   ├── @sidebar.js   // Parallel route for sidebar content 
│   │   └── @content.js  // Parallel route for main content area 
│   └── ... (other pages)
├── ... (other folders)

pages/  (root directory)
pages/dashboard/  (folder for dashboard layout)
   pages/dashboard/index.js  (layout file)
   @sidebar.js  (parallel route for sidebar content)
   @content.js  (parallel route for main content)

dashboard/index.js (Dashboard Layout)

function DashboardLayout() {
    return (
        <div className="dashboard-container">
            <aside>
                {/* Sidebar content will render here */}
            </aside>
            <main>
                {/* Main content will render here */}
            </main>
        </div>
    );
}

export default DashboardLayout;

dashboard/@sidebar.js (Parallel Route)

function Sidebar() {
    return (
        <aside>
            <h1>Navigation</h1>
            <ul>
                {/* Navigation links */}
            </ul>
        </aside>
    );
}

export default Sidebar;

dashboard/@content.js (Parallel Route)

function MainContent() {
    return (
        <main>
            <h1>Dashboard Overview</h1>
            {/* Main dashboard widgets */}
        </main>
    );
}

export default MainContent;

Explanation

  • Parallel Routes: Files with the @ prefix within the dashboard folder act as parallel routes.
  • Layout Component: dashboard/index.js serves as the layout for the dashboard, defining the overall structure (Lines 1-14).
  • Independent Rendering: The content of @sidebar.js and @content.js will be rendered independently within the placeholders defined in the DashboardLayout (e.g., within the aside and main elements Lines 4-9).
  • URL Structure: While parallel routes aid in modularization, they don’t affect the final URL in the browser. The user would simply see /dashboard.

Key Points

  • Parallel routes enable you to break down complex UIs into smaller, self-contained components.
  • Ideal for dashboards, complex layouts, or scenarios where different page sections dynamically update in parallel.

useRouter Hook in Next.js

Next.js provides a powerful mechanism for handling dynamic routes with variable URL segments. The useRouter hook, imported from next/router, allows you to access information about the current route and its dynamic segments within your page components.

Access Current Route Information

  • Get the current URL pathname.
  • Retrieve query parameters from the URL.
  • Identify dynamic segments in your routes.

Programmatic Navigation

  • router.push(url): Redirect the user to a different route within your application.
  • router.replace(url): Replace the current history entry instead of adding a new one (useful for preventing the use of the back button in certain scenarios).
  • router.back(): Navigate back to the previous page.

Use Cases

  • Fetching Data Based on Dynamic Segments: Retrieve parameters from the URL (e.g., productId) to fetch and display product-specific data.
  • Conditional Rendering: Adapt component output based on the current route.
  • Navigation Actions: Create buttons, links, or custom navigation elements to redirect users within your application.
  • Handling Form Submissions: Redirect to success pages or handle errors after form submissions.

Example: Displaying Blog Post Data

import { useRouter } from 'next/router';

function BlogPostPage() {
    const router = useRouter();
    const { postId } = router.query;

    // Fetch blog post data based on the postId 
    // ...
}

Project Structure

your-next-project/
├── pages/ 
│   ├── products/
│   │   └── [productId].js 
│   └── ... (other pages)
├── ... (other folders)

pages/products/[productId].js

import { useRouter } from 'next/router';

function ProductDetailsPage() {
    const router = useRouter();
    const { productId } = router.query;

    // Logic to fetch product data based on productId

    const handleNextProduct = () => {
        const newProductId = parseInt(productId, 10) + 1;
        if (!isNaN(newProductId)) {
            router.push(`/products/${newProductId}`);
        } else {
            console.error('Invalid productId:', productId);
        }
    };

    return (
        <div>
            <h1>Product Details: {productId}</h1>
            {/* ... */}
            <button onClick={handleNextProduct}>View Next Product</button>
        </div>
    );
}

export default ProductDetailsPage;

Explanation

  • Import the hook: Import useRouter from next/router.
  • Access Route Data:
    • Call useRouter to obtain the router object.
    • The query property on router contains dynamic route parameters (e.g., productId).
  • Fetch Data: Implement logic to fetch relevant product data based on the productId from the URL.
  • Programmatic Navigation with router.push:
    • The handleNextProduct function demonstrates how to redirect the user to the next product’s page using router.push().

    Key Points

    • The useRouter hook provides a powerful way to interact with routing in your Next.js components.
    • Access dynamic route segments for data fetching.
    • Handle user navigation actions (redirects, going back, etc.).