# CollectionGrid

> Live Shopify collection grid with filters, sort, load more and Quick Add. Bind it to a collection handle, or to the URL's collection on a collection template.

Canonical URL: https://bcc38ef904de:3005/islands/commerce/collection-grid

> Renders every product in a Shopify collection from the Storefront API, with Search & Discovery filters, sort, a crawlable "Load more" link and Quick Add per card. Filter, sort and page state live in the URL using Shopify theme parameter names (`filter.p.product_type=…`, `sort_by=…`, `page=…`).

**Category**: Commerce

## Props

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| collection | string | No | - | Collection handle. Omit or use `"$resource"` on a collection template to bind the URL's collection. |
| pageSize | number | No | 24 | Products per page (max 250). |
| initialSort | string | No | `manual` | `manual`, `best-selling`, `title-ascending`, `title-descending`, `price-ascending`, `price-descending`, `created-ascending`, `created-descending`. |
| columns | object | No | `{mobile:2, tablet:3, desktop:4}` | Columns per breakpoint. |
| aspectRatio | string | No | `4 / 5` | Card media aspect ratio. |
| filterPlacement | string | No | `chips` | `chips`, `sidebar` or `none`. The price range filter shows in the sidebar only. |
| quickAdd | boolean \| object | No | `true` | Quick Add on each card (`QuickAddConfig`). |
| openCartOnAdd | boolean | No | `true` | `false` adds silently and only updates the bag counter. |
| hideSoldOut | boolean | No | `false` | Filter out sold-out products. |
| showCompareAt | boolean | No | `false` | Strike through compare-at prices. |
| hideOnMobile | boolean | No | `false` | Render and fetch nothing below 768px. Pair with CollectionSwipe for the mobile view. |
| metafields | string[] | No | - | Metafields card templates may read, as `namespace.key`. |

## Embed Example

```html
<lx-island name="CollectionGrid">
  <script type="application/json">
{ "collection": "festive-jewellery", "pageSize": 24, "filterPlacement": "chips" }
  </script>
</lx-island>
```

## Card templates

A `<template data-slot="card">` child replaces the default card. Bind product fields with attributes; the island fills them per product and mounts managed pieces into `<lx-slot>` placeholders.

| Attribute | Effect |
|-----------|--------|
| `data-lx-product="title"` | Text content from a path: `id`, `handle`, `url`, `title`, `type`, `vendor`, `description`, `price`, `compareAt`, `available`, `images.0-3.url\|alt`, `metafields.ns.key` |
| `data-lx-product-src` / `-href` / `-alt` | Attribute from a URL path (`url`, `images.N.url`) or text path |
| `data-lx-product-if` / `-unless` | Keep or remove the element: a path, or `tag:<tag>` |
| `<lx-slot name="quick-add">` | Quick Add trigger (required while `quickAdd` is on) |
| `<lx-slot name="media">` | Photo carousel of the product's images |

```html
<lx-island name="CollectionGrid">
  <script type="application/json">{ "collection": "festive-jewellery" }</script>
  <template data-slot="card">
    <li class="card">
      <div class="card-media"><lx-slot name="media"></lx-slot></div>
      <a data-lx-product-href="url" data-lx-product="title"></a>
      <p data-lx-product="price"></p>
      <span data-lx-product-unless="available">Sold out</span>
      <lx-slot name="quick-add"></lx-slot>
    </li>
  </template>
</lx-island>
```

## Mobile and desktop

For a swipe deck on phones and the grid on desktop, place both islands in the same section:

```html
<lx-island name="CollectionGrid">
  <script type="application/json">{ "collection": "$resource", "hideOnMobile": true }</script>
</lx-island>
<lx-island name="CollectionSwipe">
  <script type="application/json">{ "collection": "$resource" }</script>
</lx-island>
```

## Styling

Style with `[data-part]` hooks: `root`, `filters`, `filter-chip`, `filter-group`, `toolbar`, `count`, `sort`, `grid`, `card`, `card-media`, `card-title`, `card-price`, `quick-add`, `pagination`, `load-more`, `empty`, `error`.
