DEV Community

Dylan Parker
Dylan Parker

Posted on

When building a modern web app, I often find myself needing a clean, reusable way to display product cards

Recently, I was working on a section for a men's t-shirt collection, and I wanted a component that felt both minimal and informative. Here’s how I built a responsive product card using React and CSS Grid, perfect for showcasing items like those found in a typical apparel shop.

First, I defined a simple product object. For this example, I’m using mock data for a classic crew neck tee. The key properties are image URL, title, price, and a short description.

const product = {
  img: 'https://via.placeholder.com/300x400',
  title: 'Classic Crew Neck Tee',
  price: '$29.99',
  description: 'Soft cotton blend for all-day comfort.'
};
Enter fullscreen mode Exit fullscreen mode

Next, I created the ProductCard component. The structure is straightforward: an image container, a text area for details, and a call-to-action (CTA) button. I used CSS Grid for the layout to keep it adaptable.

const ProductCard = ({ product }) => (
  <div className="product-card">
    <div className="product-image">
      <img src={product.img} alt={product.title} loading="lazy" />
    </div>
    <div className="product-info">
      <h3>{product.title}</h3>
      <p className="price">{product.price}</p>
      <p className="description">{product.description}</p>
      <button className="cta-button">Shop Now</button>
    </div>
  </div>
);
Enter fullscreen mode Exit fullscreen mode

For the styling, I used a grid layout that centers the card and gives it a subtle shadow. The image scales nicely with object-fit: cover, and the CTA button has a hover effect for interactivity.

.product-card {
  display: grid;
  grid-template-rows: 1fr auto;
  max-width: 320px;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  transition: transform 0.2s ease;
}

.product-card:hover {
  transform: translateY(-4px);
}

.product-image img {
  width: 100%;
  height: 300px;
  object-fit: cover;
}

.product-info {
  padding: 1rem;
  background: #fff;
}

.price {
  font-weight: bold;
  color: #2a9d8f;
}

.cta-button {
  background: #264653;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 6px;
  cursor: pointer;
  margin-top: 0.5rem;
}

.cta-button:hover {
  background: #1d3557;
}
Enter fullscreen mode Exit fullscreen mode

This component is flexible. You can easily swap the mock data with real API responses, like from a shop’s backend that lists men’s t-shirts. The grid layout ensures it works on mobile and desktop without extra media queries. Whether you’re building a full store or just a featured section, this pattern keeps your UI clean and performant. Try it out and tweak the colors to match your brand!

Top comments (2)

Collapse
 
frishay_ltd_a1987ef83aa1f profile image
Amelia

Great breakdown of the product card component! I've been using a similar pattern lately but found that adding a quick size selector dropdown inside the card improved UX significantly for apparel items. For men's t-shirts, customers often want to see size availability at a glance without clicking through.

One thing I'd suggest is adding an aspect-ratio CSS property to the image container to prevent layout shift during loading. Something like aspect-ratio: 3/4 works well for t-shirt images. Also, if you're fetching real inventory data, consider passing an inStock boolean to conditionally disable the CTA button.

For those working with larger collections, memoizing the ProductCard with React.memo can help performance when rendering multiple cards in a grid. Have you experimented with lazy loading images beyond the native loading="lazy" attribute? I've found that using Intersection Observer with a placeholder skeleton gives a smoother experience on slower connections.

The shadow and hover effect are clean - minimal but effective. One tweak I'd make is using will-change: transform on the card to hint the browser for smoother animations. Overall solid approach for any e-commerce UI.

Collapse
 
davitparkltd profile image
Davit Park

Great approach to building reusable product cards! I've been using a similar pattern for an apparel section I'm working on, but I found that adding a quick Sold out overlay can be really helpful for inventory management. I modified your component to accept an inStock boolean prop and conditionally render an overlay