Web Components · Product
<mmm-product-card>
A modular product tile. Acts as a container that hosts child components via slots — media, title, price, rating, variants, add-to-cart — and feeds them shared product data through a per-product store.
Overview
The card reads a product JSON object from data-product (typically injected by <mmm-loop> as {{item}}), creates a per-product store keyed by the product's slug, and renders its children via a shadow-DOM slot. Each child reads from the store — no per-child API calls.
Card dimensions are responsive per breakpoint: the most-specific value wins for the current viewport ({bp}-card-height / {bp}-card-width → card-height / card-width → default). The component re-applies CSS on resize whenever the active breakpoint changes.
Breakpoint thresholds: mobile ≤ 430px, tablet ≤ 768px, desktop ≤ 1280px, xl above.
When to use
- Collection & category product grids.
- Search-result listings.
- Related / recently-viewed carousels.
- Anywhere you want product children to share data without each one fetching.
Quick start
<script src="https://v2-api-production.commmerce.com/api/v1/web-component/commmerce-sdk.js?store=sample-store"></script>
<mmm-product-card data-product='{"slug":"classic-tee","name":"Classic Tee","price":29}' card-height="33.125rem">
<mmm-product-media view-mode="dual-image"></mmm-product-media>
<mmm-product-title></mmm-product-title>
<mmm-product-price></mmm-product-price>
<mmm-product-variants></mmm-product-variants>
<mmm-product-add-to-cart></mmm-product-add-to-cart>
</mmm-product-card>Attributes
data-productJSON string—Required. Product object — must include slug. Usually injected by <mmm-loop> as data-product="{{item}}". Drives the per-product store the children read from.card-heightstringautoBase card height — falls back when no per-breakpoint value matches.card-widthstring100%Base card width.card-backgroundstringtheme product bg / transparentCard background colour.xl-card-heightstring—Card height for viewports > 1280px.desktop-card-heightstring—Card height for 768px–1280px.tablet-card-heightstring—Card height for 430px–768px.mobile-card-heightstring—Card height for ≤ 430px.xl-card-widthstring—Card width for viewports > 1280px.desktop-card-widthstring—Card width for 768px–1280px.tablet-card-widthstring—Card width for 430px–768px.mobile-card-widthstring—Card width for ≤ 430px.custom-cssstring—Extra CSS appended inside the card's shadow DOM. Scoped — does not leak out.classstring—Standard HTML class list.Slots
Default slot — accepts any <mmm-product-*> child. Children read their data from the shared per-product store; you do not pass data-product to each child individually.
Events
product-store-readyelement{ slug: string, store: object }Fires after the per-product store has parsed data-product and is ready for child subscriptions.
Live preview
Examples
1. Inside a loop
<mmm-data-source api="product.getProductList" response-key="grid" payload-page-size="12">
<mmm-loop data="{{window.PRODUCT_RESPONSE_GRID.products}}">
<mmm-product-card data-product="{{item}}" desktop-card-height="32rem" mobile-card-height="24rem">
<mmm-product-media></mmm-product-media>
<mmm-product-title line-clamp="2"></mmm-product-title>
<mmm-product-price></mmm-product-price>
</mmm-product-card>
</mmm-loop>
</mmm-data-source>2. Per-breakpoint width
<mmm-product-card
data-product='{"slug":"classic-tee","name":"Classic Tee","price":29}'
xl-card-width="20rem"
desktop-card-width="18rem"
tablet-card-width="16rem"
mobile-card-width="100%">
</mmm-product-card>3. Listen for store-ready
document.querySelectorAll('mmm-product-card').forEach(card => {
card.addEventListener('product-store-ready', (e) => {
console.log('Ready:', e.detail.slug);
});
});