-50% CODE

BLOOM50

|

Product Card

E-commerce product card with ratings, pricing, wishlist, and cart functionality.

ReactTailwind CSSTypeScriptResponsiveCard

Live Preview

Interactive
Product Card.tsxLive Code
"use client";

import { useState } from "react";

interface Product {
  id: string;
  name: string;
  price: number;
  originalPrice?: number;
  image: string;
  category: string;
  rating: number;
  reviews: number;
  badge?: string;
  inStock: boolean;
}

export default function ProductCard() {
  const [isLiked, setIsLiked] = useState(false);
  const [isAddedToCart, setIsAddedToCart] = useState(false);

  const product: Product = {
    id: "1",
    name: "Wireless Headphones",
    price: 99.99,
    originalPrice: 149.99,
    image:
      "https:\/\/images.unsplash.com\/photo-1505740420928-5e560c06d30e?w=400&h=300&fit=crop",
    category: "Electronics",
    rating: 4.5,
    reviews: 128,
    badge: "Sale",
    inStock: true,
  };

  const handleAddToCart = () => {
    setIsAddedToCart(true);
    setTimeout(() => setIsAddedToCart(false), 2000);
  };

  const renderStars = (rating: number) => {
    return (
      <div className="flex items-center gap-1">
        {[1, 2, 3, 4, 5].map((star) => (
          <svg
            key={star}
            className={`w-4 h-4 ${
              star <= rating ? "text-yellow-400" : "text-gray-300"
            }`}
            fill="currentColor"
            viewBox="0 0 20 20"
          >
            <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" \/>
          <\/svg>
        ))}
      <\/div>
    );
  };

  return (
    <div className="flex items-center justify-center bg-gray-50 p-4">
      <div className="max-w-sm w-full bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden hover:shadow-lg transition-shadow duration-300">
        {\/* Image Section *\/}
        <div className="relative group">
          <img
            src={product.image}
            alt={product.name}
            className="w-full h-48 object-cover group-hover:scale-105 transition-transform duration-300"
          \/>

          {\/* Badge *\/}
          {product.badge && (
            <div
              className={`absolute top-3 left-3 px-2 py-1 rounded-lg text-xs font-medium ${
                product.badge === "Sale"
                  ? "bg-red-500 text-white"
                  : "bg-green-500 text-white"
              }`}
            >
              {product.badge}
            <\/div>
          )}

          {\/* Like Button *\/}
          <button
            onClick={() => setIsLiked(!isLiked)}
            className="absolute top-3 right-3 w-8 h-8 bg-white rounded-full flex items-center justify-center shadow-md hover:shadow-lg transition-shadow"
          >
            <svg
              className={`w-4 h-4 ${
                isLiked ? "text-red-500 fill-current" : "text-gray-400"
              }`}
              fill={isLiked ? "currentColor" : "none"}
              stroke="currentColor"
              viewBox="0 0 24 24"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
              \/>
            <\/svg>
          <\/button>

          {\/* Stock Overlay *\/}
          {!product.inStock && (
            <div className="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
              <span className="text-white font-medium bg-gray-800 px-3 py-1 rounded-lg">
                Out of Stock
              <\/span>
            <\/div>
          )}
        <\/div>

        {\/* Content Section *\/}
        <div className="p-4">
          {\/* Category *\/}
          <span className="text-xs text-blue-600 font-medium bg-blue-50 px-2 py-1 rounded-lg">
            {product.category}
          <\/span>

          {\/* Product Name *\/}
          <h3 className="mt-3 text-lg font-semibold text-gray-900">
            {product.name}
          <\/h3>

          {\/* Rating *\/}
          <div className="mt-2 flex items-center gap-2">
            {renderStars(product.rating)}
            <span className="text-sm text-gray-600">
              {product.rating} ({product.reviews})
            <\/span>
          <\/div>

          {\/* Price *\/}
          <div className="mt-3 flex items-center gap-2">
            <span className="text-xl font-bold text-gray-900">
              ${product.price}
            <\/span>
            {product.originalPrice && (
              <span className="text-sm text-gray-500 line-through">
                ${product.originalPrice}
              <\/span>
            )}
            {product.originalPrice && (
              <span className="text-sm text-red-600 font-medium">
                {Math.round((1 - product.price \/ product.originalPrice) * 100)}%
                off
              <\/span>
            )}
          <\/div>

          {\/* Add to Cart Button *\/}
          <button
            onClick={handleAddToCart}
            disabled={!product.inStock}
            className={`mt-4 w-full py-2 px-4 rounded-lg font-medium transition-colors ${
              product.inStock
                ? isAddedToCart
                  ? "bg-green-600 text-white"
                  : "bg-blue-600 hover:bg-blue-700 text-white"
                : "bg-gray-300 text-gray-500 cursor-not-allowed"
            }`}
          >
            {isAddedToCart
              ? "Added to Cart!"
              : product.inStock
              ? "Add to Cart"
              : "Out of Stock"}
          <\/button>
        <\/div>
      <\/div>
    <\/div>
  );
}

How to use

  1. 1. Copy the component code
  2. 2. Paste it into your React/Next.js project
  3. 3. Make sure you have Tailwind CSS configured
  4. 4. Customize colors, spacing, and content as needed

Dependencies

react^18.0.0
tailwindcss^3.0.0