Dynamic Routing in Next.js 13

In last couple of articles, we got a quick understanding of some of the key features of Next.js 13 including its file-system based routing. In this article, we dig a bit more into routing and talk about another key feature of Next.js 13 i.e., Dynamic Routing.

When you don’t know a certain part of the URL beforehand and want to create route from dynamic data, you can use Dynamic Routing. For example, in domain.com/posts/id, we don’t know ‘id’ beforehand that actually represents id of the post which is to be retrieved dynamically. In Next.js, the dynamic part in the route can be created by wrapping a folder’s name in square brackets, for example [id] or [slug].

Let’s extend previous article‘s app to see dynamic routing in action. So, in order to continue down here, first, go through each step as given in previous article. Then follow the instructions as given below.

Assuming you are inside the app’s parent folder (my-app), create the folder [postId] within app/(main)/posts by executing:

mkdir app/'(main)'/posts/'[postId]'

Now, create page.tsx inside this newly created folder:

touch app/'(main)'/posts/'[postId]'/page.tsx

Then, insert following code in app/(main)/posts/[postId]/page.tsx:

export default async function PostDetail({ params }: { params: { postId: string } }) {

  const post = await fetch('https://jsonplaceholder.typicode.com/posts/'+params.postId)
    .then((response) => response.json())
    
  return (
    <div className="p-4 m-4">
      <p>The ID of the post is: <strong>{post.id}</strong></p>
      <p>The Title of the post is: <strong>{post.title}</strong></p>
      <p>The details of the post are: <strong>{post.body}</strong></p>
    </div>
  )
}

And then replace code of app/(main)/posts/page.tsx with following:

import Link from 'next/link'

async function getData() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?userId=1')

  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }
  return res.json()
}

export default async function Page() {
  const data = await getData()

  return (
    <div className="p-4 m-4">
      <div className="relative overflow-x-auto shadow-md sm:rounded-lg">
        <table className="w-full text-sm text-left text-gray-500">
          <thead className="text-xs text-gray-700 uppercase bg-gray-50">
            <tr>
              <th scope="col" className="px-6 py-3">
                ID
              </th>
              <th scope="col" className="px-6 py-3">
                Title
              </th>
              <th scope="col" className="px-6 py-3">
                Action
              </th>
            </tr>
          </thead>
          <tbody>
            {data &&
              data.map((item, i) => (
                <tr key={i} className="bg-white border-b">
                  <td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
                    {item.id}
                  </td>
                  <td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
                    {item.title}
                  </td>
                  <td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap">
                    <Link href={`/posts/${item.id}`} className="text-gray-900 hover:underline">Click here for details</Link>
                  </td>
                </tr>
              ))
            }
          </tbody>
        </table>
      </div>
    </div>
  )
}

Assuming dev server is running, it’ll hot-reload on each change. Finally, enter localhost:3000/posts in the browser. Now, each record has ‘Click here for details’ link. Go ahead and click on any of them and observe the URL in address bar – the URL contains Id of post which has been accessed dynamically.

Dynamic routing can also do catch-all subsequent URL segments by adding an ellipsis inside the brackets […slug]. For example, app/category/[…slug]/page.js will not only catch /category/electronics, but also /category/electronics/cellphone, /category/electronics/cellphone/foldable, and so on.

Let’s create another folder by executing:

mkdir app/admin/'[...slug]' -p

Now, create page.tsx inside it:

touch app/admin/'[...slug]'/page.tsx

And then insert following code into app/admin/[…slug]/page.tsx:

export default async function Admin({ params }: { params: { slug: string } }) {
   
  return (
    <div className="p-4 m-4">
      <div className="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" role="alert">
        <p className="font-bold">Be Warned</p>
        <p>You are in Admin area.</p>
        <p>Your access slug was: <span className="font-bold">{params.slug}</span></p>
        <p>First segment of slug: <span className="font-bold capitalize">{params.slug[0]}</span></p>
      </div>
    </div>
  )
}

Save and then enter following URL in the browser:

http://localhost:3000/admin/hello/dark/one

1 thought on “Dynamic Routing in Next.js 13”

Leave a Comment