Web Components · Global
<mmm-loop>
The iteration primitive. Takes a data source (templated {{...}} expression, JSON array, or static HTML children) and repeats its first child once per item. Powers every product grid, blog list, and review carousel.
Overview
Use the data attribute with a Mustache-style template ({{window.PRODUCT_RESPONSE_X.products}}) inside an <mmm-data-source> wrapper. Each item is exposed to children as {{item}} (or rename via item-alias). Layout is grid-by-default with per-breakpoint columns; switch to flex with ui-type="flex".
Quick start
<script src="https://v2-api-production.commmerce.com/api/v1/web-component/commmerce-sdk.js?store=sample-store"></script>
<mmm-data-source api="product.getProductList" response-key="grid">
<mmm-loop data="{{window.PRODUCT_RESPONSE_GRID.products}}" xl-columns="4" desktop-columns="4" tablet-columns="2" mobile-columns="2" gap="1rem">
<mmm-product-card data-product="{{item}}"></mmm-product-card>
</mmm-loop>
</mmm-data-source>Core attributes
AttributeTypeDefaultDescription
datastring / template—Data source. Either a JSON-array string or a {{path}} template pointing at a global / SSR variable.ui-typestringflexflex, inline-flex, grid, inline-grid, block, inline, or inline-block.directionstringrowrow, column, row-reverse, column-reverse (aliases horizontal / vertical accepted).wrapstringwrapwrap, nowrap, or wrap-reverse.columnsnumber—Number of columns (grid).gapstring0.625remCSS gap between items.countnumberallCap the number of items rendered.item-aliasstring—Prefix used in template placeholders; e.g. item-alias="product" makes {{product.title}} resolve to item.title.background-colorstringwhiteLoop wrapper background.paddingstring0 0.375remLoop wrapper padding.debugbooleanfalsePrint render logs to console for debugging.Responsive columns & counts
Per-breakpoint overrides. Both hyphenated and lowercase-merged forms work.
AttributeTypeDefaultDescription
xl-columns / xlcolumnsnumbercolumnsColumns above ~1280px.desktop-columns / desktopcolumnsnumbercolumnsColumns 768–1280px.tablet-columns / tabletcolumnsnumbercolumnsColumns 430–768px.mobile-columns / mobilecolumnsnumbercolumnsColumns below 430px.xl-count / xlcountnumbercountItem count cap on XL viewports.desktop-count / desktopcountnumbercountDesktop item cap.tablet-count / tabletcountnumbercountTablet item cap.mobile-count / mobilecountnumbercountMobile item cap.Events
EventTargetPayload
loop-renderedelement (bubbles){ itemCount: number, id: string, hasRealData: boolean }Live preview
Examples
1. Product grid
<mmm-data-source api="product.getProductList" response-key="grid">
<mmm-loop data="{{window.PRODUCT_RESPONSE_GRID.products}}" xl-columns="4" mobile-columns="2" gap="1rem">
<mmm-product-card data-product="{{item}}"></mmm-product-card>
</mmm-loop>
</mmm-data-source>2. Capped count for "Latest 8"
<mmm-loop data="{{window.PRODUCT_RESPONSE_GRID.products}}" columns="4" count="8" gap="1rem">
<mmm-product-card data-product="{{item}}"></mmm-product-card>
</mmm-loop>3. Nested loop with aliasing
<mmm-loop data="{{window.CATEGORIES}}" item-alias="cat">
<section>
<h2>{{cat.name}}</h2>
<mmm-loop data="{{cat.products}}">
<mmm-product-card data-product="{{item}}"></mmm-product-card>
</mmm-loop>
</section>
</mmm-loop>4. React to render completion
document.querySelector('mmm-loop').addEventListener('loop-rendered', (e) => {
console.log('Rendered', e.detail.count, 'items');
});