Routing in Next.js — A Complete Beginner's Guide

Subscribe to receive the free weekly article

Next.js is a React framework that ships with all the features you need for production. Next.js enables routing in your app by using the file-system-based routing. In this guide, I will show you how routing works in Next.js.

How Routing works in Next.js?

Next.js uses the file system to enable routing in the app. Next treats automatically every file with the extensions .js, .jsx, .ts, or .tsx under the pages directory as a route. A page in Next.js is a React component that has a route based on its file name.

Consider this folder structure as an example:

├── pages
|  ├── index.js
|  ├── contact.js
|  └── my-folder
|     ├── about.js
|     └── index.js

Here, we have four pages:

Linking between pages

By default, Next.js pre-renders every page to makes your app fast and user-friendly. It uses the Link component provided by next/link to enable transitions between routes.

import Link from "next/link"

export default function IndexPage() {
  return (
    <div>
      <Link href="/contact">
        <a>My second page</a>
      </Link>
      <Link href="/my-folder/about">
        <a>My third page</a>
      </Link>
    </div>
  )
}

Here, we have two routes:

  • The first link leads to the page http://localhost:3000/contact
  • The second link leads to the page http://localhost:3000/my-folder/about

The Link component can receive several properties, but only the href attribute is required. Here, we use a <a></a> tag as a child component to link pages. But, you can use alternatively any element that supports the onClick event on the Link component.

Passing route parameters

Next.js allows you to pass route parameters and then get back the data using the useRouter hook or getInitialProps. It gives you access to the router object that contains the params.

  • index.js
import Link from "next/link"

export default function IndexPage() {
  return (
    <Link
      href={{
        pathname: "/about",
        query: { id: "test" },
      }}
    >
      <a>About page</a>
    </Link>
  )
}

As you can see here, instead of providing a string to the href attribute, we pass in an object that contains a pathname property which is the route, and a query element that holds the data.

  • about.js
import { useRouter } from "next/router"

export default function AboutPage() {
  const router = useRouter()
  const {
    query: { id },
  } = router
  return <div>About us: {id}</div>
}

Here, we import the useRouter hook to get the data passed in. Next, we pull it from the query object using destructuring.

If you are using server-side rendering, you have to use getInitialProps to get the data.

export default function AboutPage({ id }) {
  return <div>About us: {id}</div>
}

AboutPage.getInitialProps = ({ query: { id } }) => {
  return { id }
}

Dynamic routes

Next.js enables you to define dynamic routes in your app using the brackets ([param]). Instead of setting a static name on your pages, you can use a dynamic one.

Let's use this folder structure as an example:

├── pages
|  ├── index.js
|  ├── [slug].js
|  └── my-folder
|     ├── [id].js
|     └── index.js

Next.js will get the route parameters passed in and then use it as a name for the route.

  • index.js
export default function IndexPage() {
  return (
    <ul>
      <li>
        <Link href="/">
          <a>Home</a>
        </Link>
      </li>
      <li>
        <Link href="/[slug]" as="/my-slug">
          <a>First Route</a>
        </Link>
      </li>
      <li>
        <Link href="/my-folder/[id]" as="/my-folder/my-id">
          <a>Second Route</a>
        </Link>
      </li>
    </ul>
  )
}

Here, we have to define the value on the as attribute because the path is dynamic. The name of the route will be whatever you set on the as prop.

  • [slug].js
import { useRouter } from "next/router"

export default function DynamicPage() {
  const router = useRouter()
  const {
    query: { id },
  } = router
  return <div>The dynamic route is {id}</div>
}

You can get the route parameters as well with the useRouter hook on the client or getInitialProps on the server.

  • my-folder/[id].js
export default function MyDynamicPage({ example }) {
  return <div>My example is {example}</div>
}

MyDynamicPage.getInitialProps = ({ query: { example } }) => {
  return { example }
}

Here, we use getInitialProps to get the dynamic route passed in.

Dynamic nested routes

With Next.js, you can also nest dynamic routes with the brackets ([param]).

Let's consider this file structure:

├── pages
|  ├── index.js
|  └── [dynamic]
|     └── [id].js
  • index.js
export default function IndexPage() {
  return (
    <ul>
      <li>
        <Link href="/">
          <a>Home</a>
        </Link>
      </li>
      <li>
        <Link href="/[dynamic]/[id]" as="/my-folder/my-id">
          <a>Dynamic nested Route</a>
        </Link>
      </li>
    </ul>
  )
}

As you can see here, we set the dynamic values on the as attribute as we did in the previous example. But this time, we have to define the name of the folder and its file.

import { useRouter } from "next/router"

export default function DynamicPage() {
  const router = useRouter()
  const {
    query: { dynamic, id },
  } = router
  return (
    <div>
      Data: {dynamic} - {id}
    </div>
  )
}

Here, we pull out the route parameters from the query object with the useRouter hook.

That's it! Thanks for reading

Photo by Javier Allegue Barros on Unsplash