> ## Documentation Index
> Fetch the complete documentation index at: https://docs.joinmarkt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Products & Catalog

> How themes load and display public catalog data.

# Products & Catalog

How themes load and render the public catalog.

***

## Data sources

| Runtime      | Source                                                                         |
| ------------ | ------------------------------------------------------------------------------ |
| **Nunjucks** | Context: `products`, `standaloneProducts`, `groups`, `catalogItems`, `product` |
| **Vite**     | `fetchStorefront()` / `fetchStoreProducts()` from `storefront-api.ts`          |

Public HTTP (examples):

* `/api/stores/by-subdomain/{subdomain}/products`
* Storefront bootstrap payload used by Vite themes

Do **not** use `/api/dashboard/*` from a theme.

***

## Product shape (theme-facing)

Aligned with `MarktProduct` / `StoreProduct`:

| Field                   | Notes                                                         |
| ----------------------- | ------------------------------------------------------------- |
| `id`                    | CUID — use for cart / embed                                   |
| `slug`                  | URL segment `/product/:slug`                                  |
| `name`                  | Display title                                                 |
| `description`           | HTML/text body                                                |
| `image` / `images`      | Media                                                         |
| `type`                  | `single` \| `variant` \| `group`                              |
| `variants[]`            | `id`, `name`, `price`, `stock`, `quantityMin` / `quantityMax` |
| `minPrice` / `maxPrice` | Range for cards                                               |
| `stock`                 | `-1` often means unlimited (respect platform rules)           |
| `groupId`               | Group membership                                              |

### Groups

| Field                | Notes                                    |
| -------------------- | ---------------------------------------- |
| `groups`             | Product groups                           |
| `catalogItems`       | Groups first, then standalone products   |
| `standaloneProducts` | Not in any group — prefer for main grids |

***

## Nunjucks patterns

### All products

```njk theme={null}
{% for product in products %}
  <a href="{{ ('/product/' ~ product.slug) | shopUrl }}">
    {{ product.name }} — {{ product.minPrice | formatPrice(currency) }}
  </a>
{% endfor %}
```

Use `minPrice` / `maxPrice` on cards; `variants[].price` on the product page.

### Featured IDs

```njk theme={null}
{% set featured = helpers.components.products.getItemsByIds(products, ['slug-a', 'id-b']) %}
```

### Catalog with groups

```njk theme={null}
{% for item in catalogItems %}
  {% if item.kind == 'group' %}
    <h2>{{ item.group.name }}</h2>
  {% else %}
    <a href="{{ ('/product/' ~ item.product.slug) | shopUrl }}">
      {{ item.product.name }}
    </a>
  {% endif %}
{% endfor %}
```

More: [Shortcodes & Tags](/storefront/shortcodes).

***

## Vite patterns

```ts theme={null}
import {
  fetchStorefront,
  getProductBySlug,
  getProductPriceRange,
} from '@/lib/storefront-api'

const payload = await fetchStorefront()
const product = getProductBySlug(payload?.products ?? [], slug)
const { min, max } = product ? getProductPriceRange(product) : { min: 0, max: 0 }
```

Link cards to `/product/${product.slug}`.

***

## Add to cart & buy

| Surface       | Mechanism                                                 |
| ------------- | --------------------------------------------------------- |
| Vite theme    | Cart context → platform checkout                          |
| Nunjucks      | `appCart.add(productId, variantId, qty)` + `/api/v1/cart` |
| External site | `data-markt-product-id` + `Markt.init()`                  |

Never invent a payment form that posts card numbers to your theme origin.

***

## Visibility & empty states

* `private` never appears in the public payload — do not invent drafts
* `on-hold` **does** appear — show “On hold”, disable Add to cart / Buy now
* Empty catalog → friendly empty UI
* Loading → skeletons on Vite

***

## See also

* [Homepage & Sections](/storefront/homepage)
* [Nunjucks Theme API](/storefront/nunjucks)
* [Vite Storefront](/storefront/vite)
