# Liquid drops reference — catalog

Source: https://support.storeconnect.com/articles/liquid-drops-reference-catalog · Last modified 21 August 2026

This article covers the Liquid drops for StoreConnect's catalog domain: products, pricing, categories, brands, variants, and traits.

- For global/store drops, see [Liquid drops — global context](liquid-drops-reference-global).
- For cart, order, and checkout drops, see [Liquid drops — cart and order](liquid-drops-reference-cart-order).
- For content, account, and location drops, see [Liquid drops — content and accounts](liquid-drops-reference-content).

---

## ProductDrop

The primary product object. Available as `current_product` on product detail pages, and as individual items in `all_products` or category product collections.

| Property | Type | Description |
|----------|------|-------------|
| `id` | String | Product ID |
| `name` | String | Product name |
| `identifier` | String | Product identifier (alias for `slug`) |
| `slug` | String | URL slug |
| `path` | String | URL path |
| `url` | String | Full URL |
| `product_code` | String | Product/SKU code |
| `upc` | String | Universal Product Code (used as GTIN in structured data) |
| `condition` | String | Product condition: `"new"` or `"used"` |
| `summary_content` | String | Short summary HTML |
| `search_description` | String | Description used in search results and merchant feeds |
| `meta_title` | String | SEO title |
| `meta_description` | String | SEO description |
| `meta_keywords` | String | SEO keywords |
| `social_image` | ImageDrop | Social media sharing image |
| `pricing` | ProductPricingDrop | Pricing information |
| `can_purchase?` | Boolean | Can be purchased (accounts for stock, restrictions, availability) |
| `can_add_to_cart?` | Boolean | Can be added to cart |
| `out_of_stock?` | Boolean | Is out of stock |
| `out_of_stock_text` | String | Out of stock message |
| `unavailable_text` | String | Unavailable message |
| `discontinued?` | Boolean | Is discontinued |
| `track_inventory?` | Boolean | Tracks stock levels |
| `total_available_to_sell` | Number | Available quantity |
| `restricted?` | Boolean | Requires approval to purchase |
| `restricted_text` | String | Restriction message |
| `is_bundle?` | Boolean | Is a bundle product |
| `bundle_lead?` | Boolean | Is a CPQ bundle lead |
| `available_only_in_bundle?` | Boolean | Only purchasable in a bundle |
| `image` | ImageDrop | Primary image |
| `images` | Collection | All images |
| `media` | Collection | All media (images and videos) |
| `videos` | Collection | Product videos |
| `brand` | BrandDrop | Product brand |
| `categories` | Collection | Product categories |
| `tags` | Collection | Tags |
| `variants` | Paginated | Variant products (for master products) |
| `variant_types` | Collection | Variant type definitions |
| `variant_options` | Collection | Variant options |
| `variant_choices` | Collection | Variant choices |
| `variant_images` | Collection | Variant-specific images |
| `variant?` | Boolean | Is a variant (not a master) |
| `master?` | Boolean | Is a master product |
| `master` | ProductDrop | Master product (for variants) |
| `traits` | Collection | Product traits |
| `trait_groups` | Collection | Trait groups |
| `related_products` | Collection | Related products |
| `contained_in_bundles` | Collection | Bundles that contain this product |
| `custom_forms` | Collection | Associated custom forms |
| `documents` | Collection | Downloadable documents |
| `features_content` | String | Features HTML |
| `features_label` | String | Features section label |
| `specifications_content` | String | Specifications HTML |
| `specifications_label` | String | Specifications section label |
| `support_content` | String | Support HTML |
| `support_label` | String | Support section label |
| `warranty_content` | String | Warranty HTML |
| `warranty_label` | String | Warranty section label |
| `downloads_content` | String | Downloads HTML |
| `downloads_label` | String | Downloads section label |
| `add_to_cart_text` | String | Add to cart button text |
| `buy_it_now_text` | String | Buy it now button text |
| `maximum_quantity` | Number | Maximum quantity per order |
| `subscription?` | Boolean | Is a subscription product |
| `bookable?` | Boolean | Is bookable |
| `can_ship?` | Boolean | Can be shipped |
| `can_pickup?` | Boolean | Available for click & collect |
| `pricebook_entry` | PricebookEntryDrop | Pricebook entry for this product |
| `location_groups` | Collection | Location groups |
| `data` | Hash | Custom data fields |

---

## ProductPricingDrop

Detailed pricing information. Accessed via `product.pricing`.

| Property | Type | Description |
|----------|------|-------------|
| `price` | Number | Current price (sale price if on sale, otherwise original) |
| `original_price` | Number | Original (non-sale) price |
| `sale_price` | Number | Current sale price |
| `price_excl_tax` | Number | Price excluding tax (only when tax-exclusive pricing is active) |
| `sale_price_excl_tax` | Number | Sale price excluding tax |
| `price_range` | List | `[min, max]` price range for master products with variants |
| `checkout_price` | Number | Amount required at checkout |
| `has_price?` | Boolean | Has a price set |
| `hide_price?` | Boolean | Price should be hidden |
| `hide_price_text` | String | Text to display when price is hidden |
| `on_sale?` | Boolean | Currently on sale |
| `has_sale_price?` | Boolean | Has a sale price |
| `tax_inclusive?` | Boolean | Price includes tax |
| `points` | Number | Current points price |
| `original_points` | Number | Original (non-sale) points price |
| `has_sale_points?` | Boolean | Has a points sale price |
| `subscription?` | Boolean | Is a subscription price |


```liquid

{% if product.pricing.on_sale? %}
  <span class="original-price">{{ product.pricing.original_price | money }}</span>
  <span class="sale-price">{{ product.pricing.price | money }}</span>
{% else %}
  <span class="price">{{ product.pricing.price | money }}</span>
{% endif %}
```


---

## ProductCategoryDrop

A product category. Available as `current_product_category` on category pages.

| Property | Type | Description |
|----------|------|-------------|
| `id` | String | Category ID |
| `name` | String | Category name |
| `title` | String | Category title |
| `subtitle` | String | Category subtitle |
| `identifier` | String | Identifier |
| `path` | String | URL path |
| `url` | String | Full URL |
| `position` | Number | Position in category list |
| `image` | ImageDrop | Category image |
| `social_image` | ImageDrop | Social media sharing image |
| `meta_title` | String | SEO title |
| `meta_description` | String | SEO description |
| `meta_keywords` | String | SEO keywords |
| `google_product_category` | String | Google product category taxonomy ID |
| `introduction_content` | String | Introduction HTML |
| `information_content` | String | Information HTML |
| `products` | Paginated | Products in this category |
| `children` | Collection | Child categories |
| `navigation_children` | Collection | Child categories visible in navigation |
| `ancestors` | Collection | Ancestor categories (full parent chain) |
| `hidden?` | Boolean | Hidden from navigation |
| `data` | Hash | Custom data fields |

---

## BrandDrop

A product brand.

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Brand name |
| `path` | String | URL path |

---

## Variant drops

StoreConnect uses an n-ary variant system — products can have any number of variant types (Color, Size, Material, etc.).

### VariantTypeDrop

A variant dimension (e.g., "Color", "Size").

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Type name (e.g., `"Color"`) |
| `position` | Number | Sort order |
| `options` | Collection | Available options for this type |

### VariantOptionDrop

A specific value for a variant type (e.g., "Red", "Large").

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Option name (e.g., `"Red"`) |
| `variant_type` | VariantTypeDrop | Parent variant type |
| `position` | Number | Sort order |

### VariantChoiceDrop

Links a variant option to a specific product variant.

| Property | Type | Description |
|----------|------|-------------|
| `variant_option` | VariantOptionDrop | The selected option |
| `product` | ProductDrop | The variant product |

**Rendering a variant selector:**


```liquid

{% for type in current_product.variant_types %}
  <div class="variant-type">
    <label>{{ type.name }}</label>
    <div class="options">
      {% for option in type.options %}
        <button
          data-variant-option="{{ option.name }}"
          data-variant-type="{{ type.name }}">
          {{ option.name }}
        </button>
      {% endfor %}
    </div>
  </div>
{% endfor %}
```


---

## Trait drops

### TraitDrop

A product attribute/characteristic.

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Trait name |
| `value` | String | Trait value |
| `trait_type` | TraitTypeDrop | Trait type definition |

### TraitTypeDrop

A trait type definition.

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Type name |
| `trait_group` | TraitGroupDrop | Parent group |

### TraitGroupDrop

A group of related traits.

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Group name |
| `trait_types` | Collection | Trait types in this group |

### TraitCategoryDrop

A category organizing trait groups.

| Property | Type | Description |
|----------|------|-------------|
| `name` | String | Category name |
| `trait_groups` | Collection | Groups in this category |

**Rendering traits:**


```liquid

{% for group in product.trait_groups %}
  <h4>{{ group.name }}</h4>
  <dl>
    {% for trait in product.traits %}
      {% if trait.trait_type.trait_group.name == group.name %}
        <dt>{{ trait.name }}</dt>
        <dd>{{ trait.value }}</dd>
      {% endif %}
    {% endfor %}
  </dl>
{% endfor %}
```


Or use the built-in filter for the default rendering:


```liquid

{{ current_product | show_traits }}
```


---

## Common patterns

### Accessing products


```liquid

{{ current_product.name }}

{% for product in all_products %}
  {{ product.name }}
{% endfor %}

{{ all_products.my-product-slug.name }}

{% for product in current_product_category.products %}
  {{ product.name }}
{% endfor %}
```


### Product card

A minimal product card covering image, brand, name, pricing, and availability:


```liquid

<div class="product-card">
  {% if product.image %}
    <a href="{{ product.path }}">
      <img
        src="{{ product.image.medium_url }}"
        srcset="{{ product.image.thumb_url }} 240w,
                {{ product.image.medium_url }} 480w,
                {{ product.image.large_url }} 640w"
        alt="{{ product.image.alt_text | default: product.name }}"
        loading="lazy">
    </a>
  {% endif %}

  {% if product.brand %}
    <span class="product-card__brand">{{ product.brand.name }}</span>
  {% endif %}

  <h3 class="product-card__name">
    <a href="{{ product.path }}">{{ product.name }}</a>
  </h3>

  {% unless product.pricing.hide_price? %}
    {% if product.pricing.on_sale? %}
      <span class="product-card__price--original">{{ product.pricing.original_price | money }}</span>
      <span class="product-card__price--sale">{{ product.pricing.price | money }}</span>
    {% else %}
      <span class="product-card__price">{{ product.pricing.price | money }}</span>
    {% endif %}
  {% else %}
    <span class="product-card__price">{{ product.pricing.hide_price_text }}</span>
  {% endunless %}

  {% if product.out_of_stock? %}
    <span class="badge badge--out-of-stock">{{ product.out_of_stock_text | default: "Out of stock" }}</span>
  {% endif %}
</div>
```


### Image gallery


```liquid

{% if product.images.size > 0 %}
  <div class="product-gallery">
    {% assign primary = product.images | first %}
    <div class="product-gallery__primary">
      <img src="{{ primary.large_url }}" alt="{{ primary.alt_text | default: product.name }}">
    </div>

    {% if product.images.size > 1 %}
      <div class="product-gallery__thumbnails">
        {% for image in product.images %}
          <img
            src="{{ image.thumb_url }}"
            data-large="{{ image.large_url }}"
            alt="{{ image.alt_text | default: product.name }}"
            loading="lazy">
        {% endfor %}
      </div>
    {% endif %}
  </div>
{% endif %}
```


### Add to cart


```liquid

{% if product.can_add_to_cart? %}
  {% form "add-to-cart", product: product %}
    {% if product.variant_types.size > 0 %}
      {% render "products/variant_selector", product: product %}
    {% endif %}

    <input type="number"
      name="{{ form.quantity.name }}"
      value="1"
      min="1"
      {% if product.maximum_quantity > 0 %}max="{{ product.maximum_quantity }}"{% endif %}>

    <button type="submit">{{ product.add_to_cart_text | default: "Add to cart" }}</button>
  {% endform %}
{% elsif product.out_of_stock? %}
  <p>{{ product.out_of_stock_text }}</p>
{% elsif product.restricted? %}
  <p>{{ product.restricted_text }}</p>
{% endif %}
```


### Variant selector

StoreConnect uses an n-ary variant system — products can have any number of variant types (Color, Size, Material, etc.), with each type having its own options.

```
Product (master)
├── VariantType: "Color"  →  VariantOptions: Red, Blue, Green
├── VariantType: "Size"   →  VariantOptions: Small, Medium, Large
└── Variants (ProductDrops)
    ├── "Red / Small"  ← VariantChoices link options to this variant
    ├── "Red / Medium"
    └── ...
```


```liquid

{% if product.variant_types.size > 0 %}
  <div class="variant-selector" data-default-variant="{{ product.default_variant.id }}">
    {% for type in product.variant_types %}
      <div class="variant-selector__type">
        <label>{{ type.name }}</label>
        <div class="variant-selector__options">
          {% for option in type.options %}
            <button
              class="variant-selector__option"
              data-variant-type="{{ type.name }}"
              data-variant-option="{{ option.name }}">
              {{ option.name }}
            </button>
          {% endfor %}
        </div>
      </div>
    {% endfor %}
    <input type="hidden" name="variant_id" id="variant_id">
  </div>
{% endif %}
```


### Variant resolution in JavaScript

The add-to-cart form needs the selected variant's `id` in the `variant_id` field. The standard pattern uses JavaScript to:

1. Capture the customer's selections (one option per variant type).
2. Iterate through `product.variants` to find the variant whose `variant_choices` match all selections.
3. Update the hidden `variant_id` field and display the matching variant's price and availability.

To expose variant data to JavaScript, serialize the variants in a `<script>` tag:


```liquid

<script type="application/json" id="product-variants">
  [
    {% for variant in product.variants %}
      {
        "id": "{{ variant.id }}",
        "available": {{ variant.can_add_to_cart? }},
        "price": {{ variant.pricing.price }},
        "choices": [
          {% for choice in variant.variant_choices %}
            {
              "type": {{ choice.variant_option.variant_type.name | json }},
              "option": {{ choice.variant_option.name | json }}
            }{% unless forloop.last %},{% endunless %}
          {% endfor %}
        ]
      }{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ]
</script>
```


### Theme variables for variant selectors

| Key | Default | Description |
|-----|---------|-------------|
| `product.variants.selector.buttons.maximum` | `5` | Max options shown as buttons before switching to a dropdown |

### Trait rendering

Quick rendering using the built-in filter:


```liquid

{{ product | show_traits }}
```


Custom rendering grouped by category and group:


```liquid

{% if product.trait_category %}
  {% for group in product.trait_category.trait_groups %}
    <section>
      <h4>{{ group.name }}</h4>
      <dl>
        {% for type in group.trait_types %}
          {% for trait in product.traits %}
            {% if trait.trait_type.name == type.name %}
              <dt>{{ trait.name }}</dt>
              <dd>{{ trait.value }}</dd>
            {% endif %}
          {% endfor %}
        {% endfor %}
      </dl>
    </section>
  {% endfor %}
{% endif %}
```


### Related products


```liquid

{% if product.related_products.size > 0 %}
  <section class="related-products">
    <h2>Related products</h2>
    <div class="product-grid">
      {% for related in product.related_products %}
        {% render "products/card", product: related %}
      {% endfor %}
    </div>
  </section>
{% endif %}
```


### Product content sections

Rich content sections (features, specifications, support, warranty, downloads) are accessible as rendered HTML:


```liquid

{% if product.features_content %}
  <section>
    <h2>{{ product.features_label | default: "Features" }}</h2>
    {{ product.features_content }}
  </section>
{% endif %}

{% if product.specifications_content %}
  <section>
    <h2>{{ product.specifications_label | default: "Specifications" }}</h2>
    {{ product.specifications_content }}
  </section>
{% endif %}
```


Or use the typed content block filters:


```liquid

{{ product | features_content_blocks }}
{{ product | specifications_content_blocks }}
{{ product | downloads_content_blocks }}
```


### Bundle products


```liquid

{% if product.is_bundle? %}
  <p>This product is a bundle</p>
{% endif %}

{% if product.available_only_in_bundle? %}
  <p>Available only as part of a bundle</p>
  {% for bundle in product.contained_in_bundles %}
    <a href="{{ bundle.path }}">{{ bundle.name }}</a>
  {% endfor %}
{% endif %}
```

---

## Follow StoreConnect

- [Email Newsletter](https://getstoreconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://getstoreconnect.com/partners)
- [News](https://getstoreconnect.com/articles/news)
- [Events](https://getstoreconnect.com/articles/events)
- [Feature Comparison](https://getstoreconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://getstoreconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

---

StoreConnect Support — https://support.storeconnect.com/articles/liquid-drops-reference-catalog