In this tutorial, ill build a dynamic product page using Next.js and Prisma.ill fetch data based on the selected category and display it using a visually appealing card layout.
1. Setting Up Dynamic Routes
Next.js allows us to create dynamic routes easily. We'll set up a dynamic route that will render products based on the selected category.
Creating the Dynamic Route
First, create a new file under the app/products/[category]/page.tsx
directory. This file will handle rendering the products for the selected category.
app/products/[category]/page.tsx
import { notFound } from 'next/navigation';
import { type CategoryTypes } from '@prisma/client';
import React from 'react'
import prisma from '@/app/lib/db';
import ProductCard from '@/app/components/ProductCard';
async function getData(category: string) {
let input;
switch (category) {
case "template":
input = "template";
break;
case "uikit":
input = "uikit";
break;
case "icon":
input = "icon";
break;
case "all":
input = undefined;
break;
default:
return notFound();
}
const data = await prisma.product.findMany({
where: {
category: input as CategoryTypes,
},
select: {
id: true,
images: true,
smallDescription: true,
name: true,
price: true,
}
});
return data;
}
export default async function CategoryPage({ params }: { params: { category: string } }) {
const data = await getData(params.category);
return (
<section className='max-w-7xl mx-auto px-4 md:px-8'>
<div className="grid grid-cols-1 lg:grid-cols-3 sm:grid-cols-2 gap-10 mt-4">
{data.map((product) => (
<ProductCard
key={product.id}
images={product.images}
name={product.name}
smallDescription={product.smallDescription}
price={product.price}
id={product.id}
/>
))}
</div>
</section>
)
}
Explanation
-
Dynamic Routing: The
[category]
in the file name tells Next.js to treat this segment as a variable. Based on this variable, we'll fetch and display products. -
Fetching Data: The
getData
function retrieves products based on the category. It handles categories like "template", "uikit", "icon", and "all". -
Rendering Cards: The
ProductCard
component is used to display each product in a grid layout.
2. Creating the Product Card Component
To present the products, we need a ProductCard
component that displays product details in a card format.
app/components/ProductCard.tsx
import React from 'react'
import Image from 'next/image'
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel';
import Link from 'next/link';
import { Button } from '@/components/ui/button';
interface iAppProps {
id: string;
name: string;
smallDescription: string;
description?: string;
price: number;
images: string[];
}
export default function ProductCard({ id, name, smallDescription, price, images }: iAppProps) {
return (
<div className='rounded-lg p-2 w-full h-full flex flex-col justify-between items-center bg-white shadow-md'>
<Carousel>
<CarouselContent>
{images.map((item, index) => (
<CarouselItem key={index}>
<div className='relative h-[230px] w-full'>
<Image
alt='Product image'
src={item}
width={230}
height={230}
quality={75}
className='shadow-md object-cover w-full h-full rounded-lg' />
</div>
</CarouselItem>
))}
</CarouselContent>
{images.length > 1 && (
<>
<CarouselPrevious className="ml-16" />
<CarouselNext className="mr-16" />
</>
)}
</Carousel>
<div className='flex flex-col gap-y-2 p-2 items-end justify-start text-left h-full'>
<div className='flex justify-between items-start mt-2 gap-x-3 w-full'>
<h1 className="text-md font-bold text-wrap">{name}</h1>
<h3 className="inline-flex items-center rounded-full bg-primary/10 px-2 py-1 text-sm font-medium text-primary ring-1 ring-inset ring-primary/10">$<span className="font-semibold">{price}</span></h3>
</div>
<p className='text-neutral-600 text-sm text-left line-clamp-2'>{smallDescription}</p>
<div className='items-end justify-end pt-2'>
<Button className='rounded-lg hover:shadow-lg hover:scale-105 transition-all duration-300 '>
<Link href={`/product/${id}`}>View Details</Link>
</Button>
</div>
</div>
</div>
)
}
Explanation
- Image Carousel: Displays multiple images for each product. Users can navigate through images using the carousel controls.
- Product Details: Shows the product's name, price, and a brief description.
- View Details Button: Links to a detailed product view page.
3. Adding Navigation Links
To enable navigation between different categories, create a NavbarLinks
component.
app/components/NavbarLinks.tsx
"use client"
import { cn } from '@/lib/utils'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import React from 'react'
export const navdata = [
{
id: 0,
name: "Home",
href: "/",
},
{
id: 1,
name: "Templates",
href: "/products/template",
},
{
id: 2,
name: "UI Kits",
href: "/products/uikit",
},
{
id: 3,
name: "Icons",
href: "/products/icon",
},
]
export default function NavbarLinks() {
const location = usePathname();
return (
<div className='hidden md:flex justify-center items-center col-span-6 gap-x-9 text-md md:text-sm text-neutral-600'>
{navdata.map((item) => {
return (
<Link key={item.id} href={item.href} className={cn(
location === item.href ? "bg-orange-100/60 font-medium bg-opacity-100 py-1 px-2 transition-all duration-300 rounded-full text-orange-400" : "hover:text-orange-400 duration-300",
)}>
<h1>{item.name}</h1>
</Link>
)
})}
</div>
)
}
Explanation
- Navigation Links: Provides links to different product categories. Highlights the currently active category based on the URL.
Conclusion
In this tutorial, we set up a dynamic route in Next.js to display products based on the selected category. We created a ProductCard
component to present products and added a navigation bar to switch between categories.
Feel free to adapt these components and routing strategies to fit your specific project needs. Happy coding!
Top comments (0)