List Products Grid Block
ListProductsGrid is a responsive card grid and list view built on MUI Stack with Card items, supporting horizontally scrollable chip filters, line clamping, and complete compound layout customization.
Installationโ
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-list-products-grid-block
yarn add @nodeblocks/frontend-list-products-grid-block
pnpm add @nodeblocks/frontend-list-products-grid-block
bun add @nodeblocks/frontend-list-products-grid-block
What You Needโ
| Item | Why it matters |
|---|---|
items | Optional array of product card configurations for ListProductsGrid.Items.GridCard |
layout (optional) | Preset layout: responsive multi-column grid ('grid') or single-column list ('list') |
listProductsGridTitle (optional) | Main super-title text or custom React node |
subtitle (optional) | Secondary title displayed above the main title |
ListProductsGrid acts as a presentational listing block. Provide card data through items or pass children elements to render custom catalog items (see Compound Components).
Code Examplesโ
- Quick Start
- Grid and List Layouts
- Compound Components
- Block Override
function Example() { const products = [ { title: 'Handcrafted Oak Console', subtitle: 'Premium artisan collection', summary: 'Handcrafted solid-oak media console with cable management and soft-close drawers.', imageUrl: 'https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'New', color: 'warning' }], tags: [{ label: 'In Stock' }], }, { title: 'Ergonomic Desk Chair', subtitle: 'Office comfort collection', summary: 'Task chair with adjustable lumbar support, 3D armrests, and breathable mesh back.', imageUrl: 'https://images.unsplash.com/photo-1505797149-43b0069ec26b?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'Sale', color: 'primary' }], tags: [{ label: 'Free Shipping' }], } ]; return ( <div style={{ minHeight: 400 }}> <ListProductsGrid listProductsGridTitle="Featured Products" subtitle="Latest Collection" items={products} layout="grid" /> </div> ); }
Toggle dynamically between active layout presets (grid or list) using state controls.
function Example() { const [layout, setLayout] = React.useState('grid'); const products = [ { title: 'Handcrafted Oak Console', subtitle: 'Premium artisan collection ยท Ships from local warehouse', summary: 'Solid-oak media console with cable management and soft-close drawers. Perfect for living rooms.', imageUrl: 'https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'New', color: 'warning' }, { label: 'Free Shipping', color: 'success' }], tags: [{ label: 'Furniture' }], }, { title: 'Ergonomic Desk Chair', subtitle: 'Office comfort collection ยท Ergonomic standard certified', summary: 'Task chair with adjustable lumbar support, 3D armrests, and breathable mesh back for long working sessions.', imageUrl: 'https://images.unsplash.com/photo-1505797149-43b0069ec26b?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'Sale', color: 'primary' }], tags: [{ label: 'Office' }], } ]; return ( <div style={{ minHeight: 400 }}> <div style={{ marginBottom: 16, display: 'flex', gap: 8 }}> <button onClick={() => setLayout('grid')} style={{ padding: '6px 12px', cursor: 'pointer' }}>Grid View</button> <button onClick={() => setLayout('list')} style={{ padding: '6px 12px', cursor: 'pointer' }}>List View</button> </div> <ListProductsGrid listProductsGridTitle="Available Catalog" subtitle="Our Showroom" items={products.map(p => ({ ...p, direction: layout === 'list' ? { xs: 'column', md: 'row' } : 'column' }))} layout={layout} /> </div> ); }
The 'grid' layout structures items in three responsive columns on desktop, wrapping to one column on mobile viewports. The 'list' layout lists items vertically in a single full-width column. Use the direction card property to switch the media block layout horizontally or vertically.
Compose the layout explicitly using child blocks. Pass configuration properties directly to <ListProductsGrid.Title> and <ListProductsGrid.Items>.
function Example() { const products = [ { title: 'Handcrafted Oak Console', subtitle: 'Premium artisan collection', summary: 'Handcrafted solid-oak media console with cable management and soft-close drawers.', imageUrl: 'https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'New', color: 'warning' }], tags: [{ label: 'In Stock' }], }, { title: 'Ergonomic Desk Chair', subtitle: 'Office comfort collection', summary: 'Task chair with adjustable lumbar support, 3D armrests, and breathable mesh back.', imageUrl: 'https://images.unsplash.com/photo-1505797149-43b0069ec26b?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'Sale', color: 'primary' }], tags: [{ label: 'Free Shipping' }], } ]; return ( <div style={{ minHeight: 400 }}> <ListProductsGrid layout="grid"> <ListProductsGrid.Title listProductsGridTitle="Artisan Collection" subtitle="Latest Releases" /> <ListProductsGrid.Items layout="grid"> {products.map((item, idx) => ( <ListProductsGrid.Items.GridCard key={idx} {...item} /> ))} </ListProductsGrid.Items> </ListProductsGrid> </div> ); }
Prepend blocks or inject helper notices using a function child while keeping standard grid card mapping.
function Example() { const products = [ { title: 'Handcrafted Oak Console', subtitle: 'Premium artisan collection', summary: 'Handcrafted solid-oak media console with cable management and soft-close drawers.', imageUrl: 'https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&w=640&q=80', chips: [{ label: 'New', color: 'warning' }], tags: [{ label: 'In Stock' }], } ]; return ( <div style={{ minHeight: 400 }}> <ListProductsGrid listProductsGridTitle="Artisan Catalog" subtitle="Exclusive Sale" layout="grid" items={products} > {({ defaultBlocks, defaultBlockOrder }) => ({ blocks: { ...defaultBlocks, customNotice: ( <div style={{ background: '#eef4ff', border: '1px solid #cddcff', borderRadius: '8px', padding: '12px', fontSize: '14px', textAlign: 'center', }} > Members get an extra 10% discount at checkout. </div> ), }, blockOrder: ['title', 'customNotice', 'items'], })} </ListProductsGrid> </div> ); }
When to use block overrides
Use overrides to insert notifications, secondary filters, or banners between the Title and the main Items grid. The standard defaultBlockOrder is ['title', 'items'].
Important Propsโ
Core Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
items | ComponentProps<typeof ListProductsGrid.Items.GridCard>[] | No | undefined | Optional array of product card configurations rendered in the default Items layout |
layout | 'grid' | 'list' | No | 'grid' | Layout preset for the list items (grid: 3-column responsive row; list: vertical single column) |
Content Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
listProductsGridTitle | ReactNode | No | undefined | Main super-title text or custom React element displayed in Title block |
subtitle | string | No | undefined | Secondary category label displayed above the main title |
GridCard Propsโ
Configure properties directly on the product cards inside items or pass them directly to <ListProductsGrid.Items.GridCard /> subcomponents:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | - | Primary title of the product card |
subtitle | string | No | undefined | Secondary text or meta category listed below the title |
summary | ReactNode | No | undefined | Main card description; clamped to 3 lines on xs and 2 lines on sm+ with ellipsis |
imageUrl | string | No | undefined | Main image URL centered at the top of the card |
subtitleImageUrl | string | No | undefined | Small icon or avatar URL displayed next to the subtitle text |
chips | (ChipProps & { label: string })[] | No | undefined | Status tags and badges mapped inside a horizontally scrollable container |
tags | { icon?: ReactElement; label: string | string[] }[] | No | undefined | Icon tags listed below the card divider (joined by / if label is an array) |
linkProps | { href?: string; onClick?: (e: React.MouseEvent) => void; onNavigate: ((href: string) => void) | 'standard-html-link'; openInNewTab?: boolean } | No | undefined | Click configuration for card navigation |
disabled | boolean | No | false | When true, disables interaction and lowers card opacity |
direction | StackProps['direction'] | No | column in grid layout, responsive column/row in list layout | Flex alignment layout (overrides the layout-based default) |
actionArea | ReactNode | No | undefined | Optional trailing node (such as favorite or options icon button) in the title row |
cardContentProps | Partial<Omit<CardContentProps, 'children'>> | No | undefined | Overrides passed directly to the inner MUI CardContent component |
mainStackProps | Partial<StackProps> | No | undefined | Custom styling overrides for the card layout stack |
colInnerProps | Partial<StackProps> | No | undefined | Custom overrides for the interior column layout stack |
titleStackProps | Partial<StackProps> | No | undefined | Overrides passed to the title/subtitle text container |
Layout and Composition Propsโ
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | BlocksOverride | ReactNode | No | undefined | Compound JSX children or function override returning blocks and blockOrder |
spacing | StackProps['spacing'] | No | 5 | Layout spacing (theme units) between the title and catalog items |
className | string | No | undefined | Custom class name applied to the root container |
sx | SxProps | No | undefined | MUI system styles for the root Stack container |
ListProductsGrid inherits all StackProps (except children). Default block order without overrides is ['title', 'items'] (defaultBlockOrder).
Default UI Blocksโ
| Block | Built on | Notes |
|---|---|---|
ListProductsGrid (root) | Stack | Controlled flex wrapper (direction="column", spacing={5}) |
ListProductsGrid.Title | Stack + Typography | Renders subtitle super-header (variant h4) and title header (variant h1) |
ListProductsGrid.Items | Box | CSS Grid container. Spreads columns responsive to layout prop: 'grid' (3 columns) or 'list' (1 column) |
ListProductsGrid.Items.GridCard | Card | Main product layout card; nests CardMedia, HorizontalScrollEdgeArrows, and metadata columns |
TypeScriptโ
import { ListProductsGrid } from '@nodeblocks/frontend-list-products-grid-block';
import type { ComponentProps } from 'react';
type GridCardConfig = ComponentProps<typeof ListProductsGrid.Items.GridCard>;
const products: GridCardConfig[] = [
{
title: 'Solid Oak Console Table',
subtitle: 'Limited artisan release',
summary: 'A beautiful console hand-built from premium white oak.',
imageUrl: 'https://example.com/oak-table.jpg',
chips: [{ label: 'New', color: 'warning' }],
linkProps: {
href: '/products/oak-table',
onNavigate: 'standard-html-link',
}
}
];
<ListProductsGrid
listProductsGridTitle="Our Showroom"
subtitle="Handcrafted Furniture"
items={products}
layout="grid"
/>;