Cards

Product Card

An e-commerce product card with image, rating, and add to cart action.

ReactavailableIntermediate
cardproductecommerce
Author: Prism Bits @saiusesgithub
Live React preview
Loading preview…
"use client";

import Image from "next/image";

type ProductCardProps = {
  title?: string;
  price?: string;
  originalPrice?: string;
  imageUrl?: string;
  rating?: number;
  reviewCount?: number;
  onAddToCart?: () => void;
};

export default function ProductCard({
  title = "Sony Noise Cancelling Headphones",
  price = "$298.00",
  originalPrice = "$348.00",
  imageUrl = "https://images.unsplash.com/photo-1618366712010-f4ae9c647dcb?auto=format&fit=crop&w=600&q=80",
  rating = 4.8,
  reviewCount = 1240,
  onAddToCart,
}: ProductCardProps) {
  return (
    <div className="group flex w-full max-w-sm flex-col overflow-hidden rounded-2xl border border-white/12 bg-white/[0.03] shadow-2xl transition duration-300 hover:border-white/20 hover:bg-white/[0.05]">
      <div className="relative aspect-square overflow-hidden bg-white/5">
        <Image
          src={imageUrl}
          alt={title}
          fill
          className="object-cover transition duration-500 group-hover:scale-105"
        />
        <div className="absolute right-3 top-3 rounded-full bg-black/50 p-2 backdrop-blur-md transition hover:bg-white/20">
          <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-white">
            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
          </svg>
        </div>
      </div>
      
      <div className="flex flex-1 flex-col p-5">
        <div className="mb-2 flex items-center gap-1">
          {[...Array(5)].map((_, i) => (
            <svg key={i} xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill={i < Math.floor(rating) ? "currentColor" : "none"} stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={i < Math.floor(rating) ? "text-yellow-400" : "text-white/20"}>
              <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
            </svg>
          ))}
          <span className="ml-1 text-xs text-white/50">({reviewCount})</span>
        </div>
        
        <h3 className="mb-1 text-lg font-medium text-white line-clamp-1">{title}</h3>
        
        <div className="mb-5 flex items-center gap-2 mt-auto">
          <span className="text-xl font-bold text-white">{price}</span>
          {originalPrice && (
            <span className="text-sm text-white/40 line-through">{originalPrice}</span>
          )}
        </div>
        
        <button
          onClick={onAddToCart}
          className="flex w-full items-center justify-center gap-2 rounded-xl bg-white/10 px-4 py-3 text-sm font-semibold text-white transition hover:bg-white/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <circle cx="9" cy="21" r="1" />
            <circle cx="20" cy="21" r="1" />
            <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" />
          </svg>
          Add to Cart
        </button>
      </div>
    </div>
  );
}