Skip to main content

ProductList

Description

Displays an array of products in grid or list layout. Includes loading skeleton, empty state, and customizable rendering. Supports responsive columns and add to cart functionality.

Import

import { ProductList } from '@components/frontStore/catalog/ProductList';

Usage

import { ProductList } from '@components/frontStore/catalog/ProductList';

function CategoryPage({ products }) {
return (
<ProductList
products={products}
layout="grid"
gridColumns={4}
/>
);
}

Props

NameTypeRequiredDefaultDescription
productsProductData[]Yes[]Array of products to display
imageWidthnumberNo300Product image width in pixels
imageHeightnumberNo300Product image height in pixels
isLoadingbooleanNofalseShow loading skeleton
emptyMessagestring | ReactNodeNo'No products found'Empty state message
classNamestringNo''Additional CSS classes
layout'grid' | 'list'No'grid'Display layout mode
gridColumnsnumberNo4Number of columns (1-6)
showAddToCartbooleanNofalseShow add to cart buttons
customAddToCartRenderer(product) => ReactNodeNo-Custom add to cart renderer
renderItem(product) => ReactNodeNo-Custom product item renderer

Examples

Basic Grid Layout

import { ProductList } from '@components/frontStore/catalog/ProductList';

function Products({ products }) {
return (
<ProductList
products={products}
layout="grid"
gridColumns={3}
/>
);
}

List Layout

import { ProductList } from '@components/frontStore/catalog/ProductList';

function SearchResults({ products }) {
return (
<ProductList
products={products}
layout="list"
imageWidth={150}
imageHeight={150}
/>
);
}

With Loading State

import { ProductList } from '@components/frontStore/catalog/ProductList';
import { useState, useEffect } from 'react';

function CategoryPage() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetchProducts().then(data => {
setProducts(data);
setLoading(false);
});
}, []);

return (
<ProductList
products={products}
isLoading={loading}
gridColumns={4}
/>
);
}

With Add to Cart

import { ProductList } from '@components/frontStore/catalog/ProductList';

function ShopPage({ products }) {
return (
<ProductList
products={products}
showAddToCart={true}
gridColumns={4}
/>
);
}

Custom Empty Message

import { ProductList } from '@components/frontStore/catalog/ProductList';

function SearchResults({ products, searchTerm }) {
return (
<ProductList
products={products}
emptyMessage={
<div>
<p>No results found for "{searchTerm}"</p>
<a href="/products">Browse all products</a>
</div>
}
/>
);
}

Custom Product Renderer

import { ProductList } from '@components/frontStore/catalog/ProductList';
import { Image } from '@components/common/Image';

function FeaturedProducts({ products }) {
return (
<ProductList
products={products}
gridColumns={3}
renderItem={(product) => (
<div className="featured-product">
<div className="badge">Featured</div>
{product.image && (
<Image
src={product.image.url}
alt={product.name}
width={300}
height={300}
/>
)}
<h3>{product.name}</h3>
<p className="price">{product.price.regular.text}</p>
<a href={product.url}>View Details</a>
</div>
)}
/>
);
}

Custom Add to Cart Button

import { ProductList } from '@components/frontStore/catalog/ProductList';
import { AddToCart } from '@components/frontStore/cart/AddToCart';

function ProductGrid({ products }) {
return (
<ProductList
products={products}
showAddToCart={true}
customAddToCartRenderer={(product) => (
<AddToCart
product={{ sku: product.sku, isInStock: product.inventory.isInStock }}
qty={1}
>
{({ addToCart, isLoading, canAddToCart }) => (
<button
onClick={addToCart}
disabled={!canAddToCart}
className="custom-add-btn"
>
{isLoading ? 'Adding...' : 'Quick Add'}
</button>
)}
</AddToCart>
)}
/>
);
}

Complete Example

import { ProductList } from '@components/frontStore/catalog/ProductList';
import { useCategory } from '@components/frontStore/catalog/CategoryContext';
import { useState } from 'react';

function CategoryProducts() {
const category = useCategory();
const [layout, setLayout] = useState<'grid' | 'list'>('grid');
const [columns, setColumns] = useState(4);

return (
<div className="category-products">
{/* Controls */}
<div className="controls">
<div className="layout-toggle">
<button
onClick={() => setLayout('grid')}
className={layout === 'grid' ? 'active' : ''}
>
Grid
</button>
<button
onClick={() => setLayout('list')}
className={layout === 'list' ? 'active' : ''}
>
List
</button>
</div>

{layout === 'grid' && (
<select value={columns} onChange={(e) => setColumns(Number(e.target.value))}>
<option value={2}>2 Columns</option>
<option value={3}>3 Columns</option>
<option value={4}>4 Columns</option>
<option value={5}>5 Columns</option>
</select>
)}

<span>{category.products.total} products</span>
</div>

{/* Product List */}
<ProductList
products={category.products.items}
layout={layout}
gridColumns={columns}
showAddToCart={true}
imageWidth={300}
imageHeight={300}
emptyMessage="No products in this category"
className="mt-8"
/>
</div>
);
}

Grid Columns

Responsive grid columns based on gridColumns prop:

  • 1: Single column (all screens)
  • 2: 1 column mobile, 2 columns desktop
  • 3: 1 mobile, 2 tablet, 3 desktop
  • 4: 1 mobile, 2 tablet, 4 desktop (default)
  • 5: 1 mobile, 2 tablet, 5 desktop
  • 6: 1 mobile, 2 tablet, 6 desktop

List Layout

In list layout:

  • Products displayed vertically
  • Images limited to max 150x150px
  • Full width items

States

Loading State

Shows skeleton placeholders matching the layout and column count.

Empty State

Displays emptyMessage when no products exist.

Loaded State

Renders product items using default or custom renderer.

Features

  • Flexible Layout: Grid or list display
  • Responsive Grid: Auto-adjusts columns for mobile/tablet/desktop
  • Loading Skeleton: Animated placeholders
  • Empty State: Customizable no-results message
  • Custom Rendering: Full control over product display
  • Add to Cart: Built-in or custom add to cart
  • Image Sizing: Configurable dimensions
  • Type Safe: Full TypeScript support