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.
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