Advanced Routing Techniques in Next.js

Reading Time: 9 min read

Introduction

Next.js is well-known for its powerful and intuitive routing system, which allows developers to build dynamic and flexible web applications with ease. While basic routing is straightforward, advanced routing techniques can unlock new levels of functionality and efficiency. In this post, we'll delve into some advanced routing techniques in Next.js to help you create more dynamic applications.

Dynamic Routes

  1. Catch-All Routes:

    Catch-all routes enable you to match multiple segments of a URL and handle them in a single page component. This is particularly useful for building nested or hierarchical content structures.

    // pages/blog/[...slug].js
    import { useRouter } from 'next/router'
     
    const BlogPost = () => {
      const router = useRouter()
      const { slug } = router.query
     
      return (
        <div>
          <h1>Blog Post</h1>
          <p>Slug: {slug.join('/')}</p>
        </div>
      )
    }
     
    export default BlogPost
  2. Optional Catch-All Routes:

    Optional catch-all routes allow you to define routes that match either a single segment or multiple segments, giving you more flexibility in handling URLs.

    // pages/docs/[[...params]].js
    import { useRouter } from 'next/router'
     
    const Docs = () => {
      const router = useRouter()
      const { params } = router.query
     
      return (
        <div>
          <h1>Documentation</h1>
          {params ? (
            <p>Params: {params.join('/')}</p>
          ) : (
            <p>Welcome to the Docs</p>
          )}
        </div>
      )
    }
     
    export default Docs

API Routes

  1. Dynamic API Routes:

    Similar to dynamic page routes, you can create dynamic API routes to handle different endpoints with a single function.

    // pages/api/user/[id].js
    export default function handler(req, res) {
      const { id } = req.query
      res.status(200).json({ userId: id })
    }
  2. Middleware in API Routes:

    Middleware functions can be used in API routes to handle tasks such as authentication, logging, or rate limiting.

    // pages/api/secure/[id].js
    import { verifyToken } from '../../utils/auth'
     
    export default function handler(req, res) {
      const token = req.headers.authorization
      if (!verifyToken(token)) {
        return res.status(401).json({ message: 'Unauthorized' })
      }
     
      const { id } = req.query
      res.status(200).json({ userId: id })
    }

Custom Server

  1. Custom Express Server:

    For more control over your routing and server-side logic, you can use a custom server with Express. This allows you to define custom routes and middleware.

    // server.js
    const express = require('express')
    const next = require('next')
     
    const dev = process.env.NODE_ENV !== 'production'
    const app = next({ dev })
    const handle = app.getRequestHandler()
     
    app.prepare().then(() => {
      const server = express()
     
      server.get('/p/:id', (req, res) => {
        const actualPage = '/post'
        const queryParams = { id: req.params.id }
        app.render(req, res, actualPage, queryParams)
      })
     
      server.all('*', (req, res) => {
        return handle(req, res)
      })
     
      server.listen(3000, (err) => {
        if (err) throw err
        console.log('> Ready on http://localhost:3000')
      })
    })

Custom 404 Page

  1. Custom 404 Page:

    You can create a custom 404 page to handle not-found routes and provide a better user experience.

    // pages/404.js
    export default function Custom404() {
      return (
        <div>
          <h1>404 - Page Not Found</h1>
          <p>Sorry, we couldn't find the page you're looking for.</p>
        </div>
      )
    }

Linking Between Routes

  1. Using the Link Component:

    The Next.js Link component provides a way to navigate between routes while maintaining the single-page application (SPA) experience.

    // pages/index.js
    import Link from 'next/link'
     
    export default function Home() {
      return (
        <div>
          <h1>Home Page</h1>
          <Link href="/about">
            <a>Go to About</a>
          </Link>
        </div>
      )
    }
  2. Programmatic Navigation:

    Use the useRouter hook to navigate programmatically based on user actions or events.

    // pages/contact.js
    import { useRouter } from 'next/router'
     
    export default function Contact() {
      const router = useRouter()
     
      const handleSubmit = (event) => {
        event.preventDefault()
        // Handle form submission logic
        router.push('/thank-you')
      }
     
      return (
        <div>
          <h1>Contact Us</h1>
          <form onSubmit={handleSubmit}>
            <input type="text" placeholder="Your Name" required />
            <button type="submit">Submit</button>
          </form>
        </div>
      )
    }

Conclusion

Next.js offers a powerful and flexible routing system that can handle a wide range of use cases, from simple static routes to complex dynamic routing and custom server logic. By leveraging these advanced routing techniques, you can build more dynamic, efficient, and user-friendly web applications. Start experimenting with these techniques in your Next.js projects to take full advantage of what the framework has to offer.

For more detailed information, visit the Next.js documentation on routing.

Go back Home.