bloomtpl
  • Documentation
  • Contact
bloomtpl

The best Next.js templates for modern web applications.
Deploy faster, code less.

Your Feedback

Review us onTrustpilot

Templates

  • All Templates10+
  • Business Templates
  • E-Commerce Templates
  • Landing Page Templates
  • Blog Templates
  • Portfolio Templates
  • Free Templates

Resources

  • Docs
  • Contact
  • Blog
  • License

Follow us

Twitter

Legal

  • Privacy Policy
  • Refund Policy
  • Terms of Use
  • Legal Notice
© 2025 BloomTPL™. All Rights Reserved.
❤️Made with love for developers
ComponentsProduct Card

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
tailwindcss
Back to Components

Need complete templates?

View Premium Templates