javascriptnext.jsnext.js13dynamic-routingnextjs-dynamic-routing

setup dynamic routes using next js


I'm trying to build a Next.js application that displays a catalog of products organized by categories and subcategories. The category and subcategory data is stored in an array of objects that looks like this:

const categories = [
  {
    id: 1,
    name: "Category A",
    subcategories: [
      {
        id: 1,
        name: "Subcategory A1",
        subsubcategories: [
          {
            id: 1,
            name: "Sub-Subcategory A1.1",
          },
          {
            id: 2,
            name: "Sub-Subcategory A1.2",
          },
        ],
      },
      {
        id: 2,
        name: "Subcategory A2",
        subsubcategories: [
          {
            id: 3,
            name: "Sub-Subcategory A2.1",
          },
          {
            id: 4,
            name: "Sub-Subcategory A2.2",
          },
        ],
      },
    ],
  },
  {
    id: 2,
    name: "Category B",
    subcategories: [
      {
        id: 3,
        name: "Subcategory B1",
        subsubcategories: [
          {
            id: 5,
            name: "Sub-Subcategory B1.1",
          },
          {
            id: 6,
            name: "Sub-Subcategory B1.2",
          },
        ],
      },
      {
        id: 4,
        name: "Subcategory B2",
        subsubcategories: [
          {
            id: 7,
            name: "Sub-Subcategory B2.1",
          },
          {
            id: 8,
            name: "Sub-Subcategory B2.2",
          },
        ],
      },
    ],
  },
];

I want to be able to generate dynamic paths for each product that follow the pattern category/subcategory/subsubcategory/productID. For example, if the user clicks on a product with ID 123 in Category A, Subcategory A1, and Sub-Subcategory A1.1, the URL should look like this: /category-a/subcategory-a1/sub-subcategory-a1-1/123.

The product data is stored in a separate array of objects that looks like this:

const products = [
  {
    id: 123,
    name: "Product A1.1.1",
    category_id: 1,
    subcategory_id: 1,
    subsubcategory_id: 1,
    description: "Lorem ipsum dolor sit amet",
    price: 9.99,
    image: "/images/product-a1-1-1.jpg",
  },]

category-a/subcategory-a1/sub-subcategory-a1-1/123.


Solution

  • Try this:

    // file: /pages/[category]/[subcategory]/[subsubcategory]/[product].js

    import React from 'react'
    import { useRouter } from 'next/router'
    
        const items = [
          {
            id: 1,
            name: "Banana",
            category: 1,
            subcategory: 1,
            subsubcategory: 1,
          },
          {
            id: 2,
            name: "Foobar",
            category: 2,
            subcategory: 3,
            subsubcategory: 5,
          }
        ];
        
        
        const categories = [
          {
            id: 1,
            name: "Fruits",
            subcategories: [
              {
                id: 1,
                name: "Tropical fruits",
                subsubcategories: [
                  {
                    id: 1,
                    name: "Berry fruits", // yes a banana is really a berry fruit
                  },
                  {
                    id: 2,
                    name: "Sub-Subcategory A1.2",
                  },
                ],
              },
              {
                id: 2,
                name: "Subcategory A2",
                subsubcategories: [
                  {
                    id: 3,
                    name: "Sub-Subcategory A2.1",
                  },
                  {
                    id: 4,
                    name: "Sub-Subcategory A2.2",
                  },
                ],
              },
            ],
          },
          {
            id: 2,
            name: "Category B",
            subcategories: [
              {
                id: 3,
                name: "Subcategory B1",
                subsubcategories: [
                  {
                    id: 5,
                    name: "Sub-Subcategory B1.1",
                  },
                  {
                    id: 6,
                    name: "Sub-Subcategory B1.2",
                  },
                ],
              },
              {
                id: 4,
                name: "Subcategory B2",
                subsubcategories: [
                  {
                    id: 7,
                    name: "Sub-Subcategory B2.1",
                  },
                  {
                    id: 8,
                    name: "Sub-Subcategory B2.2",
                  },
                ],
              },
            ],
          },
        ];
        
        const slugify = str =>
          str
            .toLowerCase()
            .trim()
            .replace(/[^\w\s-]/g, '')
            .replace(/[\s_-]+/g, '-')
            .replace(/^-+|-+$/g, '');
        
        export default function Product() {
        
          const router = useRouter()
        
          const { category, subcategory, subsubcategory, product } = router.query
          console.log(category, subcategory, subsubcategory, product);
          // {category: 'fruits', subcategory: 'tropical-fruits', subsubcategory: 'berry-fruits', product: 'banana'}
        
        
          const found = items.find((item) => slugify(item.name) === product); // banana === banana
          console.log({ found });
          // {id: 1, name: 'Banana', category: 1, subcategory: 1, subsubcategory: 1}
        
          return (
            <div>{found.name}</div>
          )
        }
        
        export async function getStaticPaths() {
        
          const paths = items.map((item) => {
            // find the right categories for this product item
            let categoryFound = categories.find((c) => c.id == item.category);
            let subcategoryFound = categoryFound.subcategories.find((sc) => sc.id == item.subcategory);
            let subsubcategoryFound = subcategoryFound.subsubcategories.find((ssc) => ssc.id == item.subsubcategory);
        
            return {
              params: {
                product: slugify(item.name), // the slug
                category: slugify(categoryFound.name),
                subcategory: slugify(subcategoryFound.name),
                subsubcategory: slugify(subsubcategoryFound.name)
              }
            }
          });
        
          return {
            paths,
            fallback: false,
          };
        
        }
        
        export async function getStaticProps({ params }) {
        
          const { category, subcategory, subsubcategory, product } = params;
        
          return {
            props: {
              category,
              subcategory,
              subsubcategory,
              product,
            },
          };
        }
    

    The build process also build the product pages with the correct categories: image

    And you can open your products like so:

    http://localhost:3000/fruits/tropical-fruits/berry-fruits/banana