List Products Grid Block
The ListProductsGrid Component is a fully customizable and accessible grid layout for displaying products built with React and TypeScript. It provides a complete product listing interface with modern design patterns, flexible customization options, and composable sub-components for titles, items, and cards.
π Installationβ
npm install @nodeblocks/frontend-list-products-grid-block@0.1.2
π Usageβ
import {ListProductsGrid} from '@nodeblocks/frontend-list-products-grid-block';
- Basic Usage
- Advanced Usage
function ProductsGridExample() {
const data: ComponentProps<typeof ListProductsGrid.Items.GridCard>[] = [
{
chips: [{ label: 'New' }],
tags: [{ icon: 'shopping_bag', label: 'Category 1' }],
subtitleImageUrl: '',
summary: 'This is a summary of something',
imageUrl: 'https://unsplash.it/640?random&x=' + Math.random(),
subtitle: 'Product Card subtitle 1',
title: 'Product Card Title 1',
linkProps: {
href: '/products/1',
onNavigate: () => console.log('Navigating to product 1')
}
},
{
chips: [{ label: 'New' }],
tags: [{ icon: 'shopping_bag', label: 'Category 1' }],
linkProps: { href: '/my/product/card2', onNavigate: () => {} },
imageUrl: 'https://unsplash.it/640?random&x=' + Math.random(),
subtitle: 'Product Card subtitle 2',
title: 'Product Card Title 2',
},
{
chips: [{ label: 'New' }],
tags: [{ icon: 'shopping_bag', label: ['Category 1', 'Category 2'] }],
linkProps: { href: '/my/product/card3', onNavigate: () => {} },
imageUrl: 'https://unsplash.it/640?random&x=' + Math.random(),
subtitle: 'Product Card subtitle 3',
title: 'Product Card Title 3',
},
];
return (
<ListProductsGrid
listProductsGridTitle="Our Products"
subtitle="Featured Items"
className="custom-grid">
<ListProductsGrid.Title />
<ListProductsGrid.Items>
{data.map((props, i) => (
<ListProductsGrid.Items.GridCard key={i} {...props} />
))}
</ListProductsGrid.Items>
</ListProductsGrid>
);
}
function CustomProductsGrid() {
const categories = [
{
name: 'Electronics',
icon: 'π»',
products: [
{
title: 'Gaming Laptop',
price: '$1,299',
rating: 4.8,
image: 'https://unsplash.it/300?random&electronics=1',
badge: 'Best Seller'
},
{
title: 'Wireless Headphones',
price: '$299',
rating: 4.6,
image: 'https://unsplash.it/300?random&electronics=2',
badge: 'New'
}
]
},
{
name: 'Fashion',
icon: 'π',
products: [
{
title: 'Designer Jacket',
price: '$199',
rating: 4.9,
image: 'https://unsplash.it/300?random&fashion=1',
badge: 'Trending'
},
{
title: 'Premium Sneakers',
price: '$149',
rating: 4.7,
image: 'https://unsplash.it/300?random&fashion=2',
badge: 'Limited'
}
]
}
];
return (
<ListProductsGrid
listProductsGridTitle="Product Categories"
subtitle="Shop by Category"
className="category-grid">
{({ defaultBlocks, defaultBlockOrder }) => ({
blocks: {
title: defaultBlocks.title,
// Complete override of items section with category-based layout
items: (
<div style={{
display: 'grid',
gap: '32px'
}}>
{categories.map((category, categoryIndex) => (
<div key={categoryIndex} style={{
background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
borderRadius: '20px',
padding: '32px',
color: 'white'
}}>
<div style={{
display: 'flex',
alignItems: 'center',
marginBottom: '24px',
gap: '16px'
}}>
<span style={{ fontSize: '32px' }}>{category.icon}</span>
<h2 style={{
fontSize: '28px',
fontWeight: 'bold',
margin: 0
}}>
{category.name}
</h2>
</div>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
gap: '20px'
}}>
{category.products.map((product, productIndex) => (
<div key={productIndex} style={{
background: 'rgba(255,255,255,0.15)',
borderRadius: '16px',
padding: '20px',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255,255,255,0.2)',
transition: 'all 0.3s ease',
cursor: 'pointer'
}}
onMouseOver={(e) => {
e.currentTarget.style.transform = 'translateY(-8px)';
e.currentTarget.style.boxShadow = '0 20px 40px rgba(0,0,0,0.2)';
}}
onMouseOut={(e) => {
e.currentTarget.style.transform = 'translateY(0)';
e.currentTarget.style.boxShadow = 'none';
}}>
<div style={{
position: 'relative',
marginBottom: '16px',
borderRadius: '12px',
overflow: 'hidden',
height: '200px'
}}>
<img
src={product.image}
alt={product.title}
style={{
width: '100%',
height: '100%',
objectFit: 'cover'
}}
/>
<div style={{
position: 'absolute',
top: '12px',
right: '12px',
background: 'rgba(255,255,255,0.9)',
color: '#333',
padding: '6px 12px',
borderRadius: '20px',
fontSize: '12px',
fontWeight: '600'
}}>
{product.badge}
</div>
</div>
<h3 style={{
fontSize: '18px',
fontWeight: '600',
marginBottom: '8px',
margin: '0 0 8px 0'
}}>
{product.title}
</h3>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '16px'
}}>
<span style={{
fontSize: '20px',
fontWeight: 'bold',
color: '#fff'
}}>
{product.price}
</span>
<div style={{
display: 'flex',
alignItems: 'center',
gap: '4px'
}}>
<span>β</span>
<span style={{ fontSize: '14px' }}>
{product.rating}
</span>
</div>
</div>
<button style={{
width: '100%',
background: 'rgba(255,255,255,0.2)',
border: '1px solid rgba(255,255,255,0.3)',
borderRadius: '8px',
padding: '12px',
color: 'white',
fontSize: '14px',
fontWeight: '600',
cursor: 'pointer',
transition: 'all 0.3s ease'
}}
onMouseOver={(e) => {
e.target.style.background = 'rgba(255,255,255,0.3)';
}}
onMouseOut={(e) => {
e.target.style.background = 'rgba(255,255,255,0.2)';
}}>
Add to Cart
</button>
</div>
))}
</div>
<div style={{
textAlign: 'center',
marginTop: '24px'
}}>
<button style={{
background: 'rgba(255,255,255,0.2)',
border: '2px solid rgba(255,255,255,0.3)',
borderRadius: '25px',
padding: '12px 32px',
color: 'white',
fontSize: '16px',
fontWeight: '600',
cursor: 'pointer',
backdropFilter: 'blur(10px)',
transition: 'all 0.3s ease'
}}
onMouseOver={(e) => {
e.target.style.background = 'rgba(255,255,255,0.3)';
e.target.style.transform = 'translateY(-2px)';
}}
onMouseOut={(e) => {
e.target.style.background = 'rgba(255,255,255,0.2)';
e.target.style.transform = 'translateY(0)';
}}>
View All {category.name} β
</button>
</div>
</div>
))}
</div>
)
},
blockOrder: ['title', 'items']
})}
</ListProductsGrid>
);
}
π§ Props Referenceβ
Main Component Propsβ
The main ListProductsGrid component inherits all props from the HTML div element (except children which is overridden) and adds:
| Prop | Type | Default | Description |
|---|---|---|---|
listProductsGridTitle | ReactNode | undefined | Main title for the product list |
subtitle | string | undefined | Secondary title displayed above the main title |
className | string | undefined | Additional CSS class name for styling the grid container |
children | BlocksOverride | undefined | Custom block components to override default rendering |
Sub-Componentsβ
The ListProductsGrid component provides several sub-components. All sub-components receive their default values from the main component's context and can override these values through props.
ListProductsGrid.Titleβ
| Prop | Type | Default | Description |
|---|---|---|---|
listProductsGridTitle | ReactNode | From context | Content to display as the main title |
subtitle | string | From context | Content to display as the subtitle |
direction | enum | "column" | Flex direction for action buttons |
alignItems | enum | "stretch" | Alignment of items in the container |
gapSize | enum | "S" | Gap between items in the container |
children | ReactNode | Default title layout | Custom content to override the default title layout |
className | string | undefined | Additional CSS class name for styling the title container |
ListProductsGrid.Itemsβ
| Prop | Type | Default | Description |
|---|---|---|---|
direction | enum | "column" | Flex direction for action buttons |
alignItems | enum | "stretch" | Alignment of items in the container |
gapSize | enum | "S" | Gap between items in the container |
children | ReactNode | Default title layout | Custom content to override the default items card layout |
className | string | undefined | Additional CSS class name for styling the items container |
ListProductsGrid.Items.GridCardβ
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | undefined | Main title displayed on the card |
subtitle | string | undefined | Secondary text displayed below the title |
summary | string | undefined | Brief description or summary text |
imageUrl | string | undefined | URL of the main image displayed on the card |
subtitleImageUrl | string | undefined | URL of a small image displayed next to the subtitle |
chips | Array<{label: string}> | undefined | Array of chip objects to display as badges |
tags | Array<{icon: enum, label: string | string[]}> | undefined | Array of tag objects with icons and labels |
linkProps | {href: string, onNavigate: () => void} | undefined | Navigation properties for making the card clickable |
className | string | undefined | Additional CSS class name for styling the card |
children | ReactNode | undefined | Custom content to display inside the card (overrides default layout) |
π§ TypeScript Supportβ
Full TypeScript support with comprehensive type definitions:
import {ListProductsGrid} from '@nodeblocks/frontend-list-products-grid-block';
import {ComponentProps} from 'react';
// Default grid card data structure
interface DefaultGridCardData {
title?: string;
subtitle?: string;
summary?: string;
imageUrl?: string;
subtitleImageUrl?: string;
chips?: Array<{label: string}>;
tags?: Array<{icon: string, label: string | string[]}>;
linkProps?: {href: string, onNavigate: () => void};
}
// Extend with custom fields
interface CustomGridCardData extends DefaultGridCardData {
category?: string;
price?: number;
rating?: number;
customField?: string;
}
const MyProductsGrid = () => {
const products: ComponentProps<typeof ListProductsGrid.Items.GridCard>[] = [
{
title: 'Product 1',
subtitle: 'Premium Quality',
summary: 'This is a detailed description of product 1',
imageUrl: 'https://example.com/product1.jpg',
chips: [{ label: 'New' }, { label: 'Featured' }],
tags: [
{ icon: 'shopping_bag', label: 'Electronics' },
{ icon: 'star', label: ['Best Seller', 'Top Rated'] }
],
linkProps: {
href: '/products/1',
onNavigate: () => console.log('Navigating to product 1')
}
},
// ... more products
];
return (
<ListProductsGrid
listProductsGridTitle="Our Products"
subtitle="Featured Collection"
className="custom-grid">
<ListProductsGrid.Title />
<ListProductsGrid.Items>
{products.map((props, index) => (
<ListProductsGrid.Items.GridCard key={index} {...props} />
))}
</ListProductsGrid.Items>
</ListProductsGrid>
);
};
Built with β€οΈ using React, TypeScript, and modern web standards.