{"title":"Liquid drops reference — catalog","slug":"liquid-drops-reference-catalog","url":"https://support.storeconnect.com/articles/liquid-drops-reference-catalog","url_markdown":"https://support.storeconnect.com/articles/liquid-drops-reference-catalog.md","subtitle":null,"summary":"Reference for the catalog-related Liquid drops in StoreConnect: ProductDrop, ProductPricingDrop, ProductCategoryDrop, BrandDrop, the variant system (VariantTypeDrop, VariantOptionDrop, VariantChoiceDrop), and the trait system (TraitDrop, TraitTypeDrop, TraitGroupDrop, TraitCategoryDrop).","type":"Developer_Documentation","video_url":"","keywords":"ProductDrop, ProductPricingDrop, ProductCategoryDrop, BrandDrop, VariantTypeDrop, VariantOptionDrop, VariantChoiceDrop, TraitDrop, TraitGroupDrop, liquid drops, catalog drops, product variants, product traits, liquid reference","last_modified":"2026-08-21T07:12:35+0000","body_markdown":"This article covers the Liquid drops for StoreConnect's catalog domain: products, pricing, categories, brands, variants, and traits.\n\n- For global/store drops, see [Liquid drops — global context](liquid-drops-reference-global).\n- For cart, order, and checkout drops, see [Liquid drops — cart and order](liquid-drops-reference-cart-order).\n- For content, account, and location drops, see [Liquid drops — content and accounts](liquid-drops-reference-content).\n\n---\n\n## ProductDrop\n\nThe primary product object. Available as `current_product` on product detail pages, and as individual items in `all_products` or category product collections.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `id` | String | Product ID |\n| `name` | String | Product name |\n| `identifier` | String | Product identifier (alias for `slug`) |\n| `slug` | String | URL slug |\n| `path` | String | URL path |\n| `url` | String | Full URL |\n| `product_code` | String | Product/SKU code |\n| `upc` | String | Universal Product Code (used as GTIN in structured data) |\n| `condition` | String | Product condition: `\"new\"` or `\"used\"` |\n| `summary_content` | String | Short summary HTML |\n| `search_description` | String | Description used in search results and merchant feeds |\n| `meta_title` | String | SEO title |\n| `meta_description` | String | SEO description |\n| `meta_keywords` | String | SEO keywords |\n| `social_image` | ImageDrop | Social media sharing image |\n| `pricing` | ProductPricingDrop | Pricing information |\n| `can_purchase?` | Boolean | Can be purchased (accounts for stock, restrictions, availability) |\n| `can_add_to_cart?` | Boolean | Can be added to cart |\n| `out_of_stock?` | Boolean | Is out of stock |\n| `out_of_stock_text` | String | Out of stock message |\n| `unavailable_text` | String | Unavailable message |\n| `discontinued?` | Boolean | Is discontinued |\n| `track_inventory?` | Boolean | Tracks stock levels |\n| `total_available_to_sell` | Number | Available quantity |\n| `restricted?` | Boolean | Requires approval to purchase |\n| `restricted_text` | String | Restriction message |\n| `is_bundle?` | Boolean | Is a bundle product |\n| `bundle_lead?` | Boolean | Is a CPQ bundle lead |\n| `available_only_in_bundle?` | Boolean | Only purchasable in a bundle |\n| `image` | ImageDrop | Primary image |\n| `images` | Collection | All images |\n| `media` | Collection | All media (images and videos) |\n| `videos` | Collection | Product videos |\n| `brand` | BrandDrop | Product brand |\n| `categories` | Collection | Product categories |\n| `tags` | Collection | Tags |\n| `variants` | Paginated | Variant products (for master products) |\n| `variant_types` | Collection | Variant type definitions |\n| `variant_options` | Collection | Variant options |\n| `variant_choices` | Collection | Variant choices |\n| `variant_images` | Collection | Variant-specific images |\n| `variant?` | Boolean | Is a variant (not a master) |\n| `master?` | Boolean | Is a master product |\n| `master` | ProductDrop | Master product (for variants) |\n| `traits` | Collection | Product traits |\n| `trait_groups` | Collection | Trait groups |\n| `related_products` | Collection | Related products |\n| `contained_in_bundles` | Collection | Bundles that contain this product |\n| `custom_forms` | Collection | Associated custom forms |\n| `documents` | Collection | Downloadable documents |\n| `features_content` | String | Features HTML |\n| `features_label` | String | Features section label |\n| `specifications_content` | String | Specifications HTML |\n| `specifications_label` | String | Specifications section label |\n| `support_content` | String | Support HTML |\n| `support_label` | String | Support section label |\n| `warranty_content` | String | Warranty HTML |\n| `warranty_label` | String | Warranty section label |\n| `downloads_content` | String | Downloads HTML |\n| `downloads_label` | String | Downloads section label |\n| `add_to_cart_text` | String | Add to cart button text |\n| `buy_it_now_text` | String | Buy it now button text |\n| `maximum_quantity` | Number | Maximum quantity per order |\n| `subscription?` | Boolean | Is a subscription product |\n| `bookable?` | Boolean | Is bookable |\n| `can_ship?` | Boolean | Can be shipped |\n| `can_pickup?` | Boolean | Available for click \u0026 collect |\n| `pricebook_entry` | PricebookEntryDrop | Pricebook entry for this product |\n| `location_groups` | Collection | Location groups |\n| `data` | Hash | Custom data fields |\n\n---\n\n## ProductPricingDrop\n\nDetailed pricing information. Accessed via `product.pricing`.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `price` | Number | Current price (sale price if on sale, otherwise original) |\n| `original_price` | Number | Original (non-sale) price |\n| `sale_price` | Number | Current sale price |\n| `price_excl_tax` | Number | Price excluding tax (only when tax-exclusive pricing is active) |\n| `sale_price_excl_tax` | Number | Sale price excluding tax |\n| `price_range` | List | `[min, max]` price range for master products with variants |\n| `checkout_price` | Number | Amount required at checkout |\n| `has_price?` | Boolean | Has a price set |\n| `hide_price?` | Boolean | Price should be hidden |\n| `hide_price_text` | String | Text to display when price is hidden |\n| `on_sale?` | Boolean | Currently on sale |\n| `has_sale_price?` | Boolean | Has a sale price |\n| `tax_inclusive?` | Boolean | Price includes tax |\n| `points` | Number | Current points price |\n| `original_points` | Number | Original (non-sale) points price |\n| `has_sale_points?` | Boolean | Has a points sale price |\n| `subscription?` | Boolean | Is a subscription price |\n\n\n```liquid\n\n{% if product.pricing.on_sale? %}\n  \u003cspan class=\"original-price\"\u003e{{ product.pricing.original_price | money }}\u003c/span\u003e\n  \u003cspan class=\"sale-price\"\u003e{{ product.pricing.price | money }}\u003c/span\u003e\n{% else %}\n  \u003cspan class=\"price\"\u003e{{ product.pricing.price | money }}\u003c/span\u003e\n{% endif %}\n```\n\n\n---\n\n## ProductCategoryDrop\n\nA product category. Available as `current_product_category` on category pages.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `id` | String | Category ID |\n| `name` | String | Category name |\n| `title` | String | Category title |\n| `subtitle` | String | Category subtitle |\n| `identifier` | String | Identifier |\n| `path` | String | URL path |\n| `url` | String | Full URL |\n| `position` | Number | Position in category list |\n| `image` | ImageDrop | Category image |\n| `social_image` | ImageDrop | Social media sharing image |\n| `meta_title` | String | SEO title |\n| `meta_description` | String | SEO description |\n| `meta_keywords` | String | SEO keywords |\n| `google_product_category` | String | Google product category taxonomy ID |\n| `introduction_content` | String | Introduction HTML |\n| `information_content` | String | Information HTML |\n| `products` | Paginated | Products in this category |\n| `children` | Collection | Child categories |\n| `navigation_children` | Collection | Child categories visible in navigation |\n| `ancestors` | Collection | Ancestor categories (full parent chain) |\n| `hidden?` | Boolean | Hidden from navigation |\n| `data` | Hash | Custom data fields |\n\n---\n\n## BrandDrop\n\nA product brand.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Brand name |\n| `path` | String | URL path |\n\n---\n\n## Variant drops\n\nStoreConnect uses an n-ary variant system — products can have any number of variant types (Color, Size, Material, etc.).\n\n### VariantTypeDrop\n\nA variant dimension (e.g., \"Color\", \"Size\").\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Type name (e.g., `\"Color\"`) |\n| `position` | Number | Sort order |\n| `options` | Collection | Available options for this type |\n\n### VariantOptionDrop\n\nA specific value for a variant type (e.g., \"Red\", \"Large\").\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Option name (e.g., `\"Red\"`) |\n| `variant_type` | VariantTypeDrop | Parent variant type |\n| `position` | Number | Sort order |\n\n### VariantChoiceDrop\n\nLinks a variant option to a specific product variant.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `variant_option` | VariantOptionDrop | The selected option |\n| `product` | ProductDrop | The variant product |\n\n**Rendering a variant selector:**\n\n\n```liquid\n\n{% for type in current_product.variant_types %}\n  \u003cdiv class=\"variant-type\"\u003e\n    \u003clabel\u003e{{ type.name }}\u003c/label\u003e\n    \u003cdiv class=\"options\"\u003e\n      {% for option in type.options %}\n        \u003cbutton\n          data-variant-option=\"{{ option.name }}\"\n          data-variant-type=\"{{ type.name }}\"\u003e\n          {{ option.name }}\n        \u003c/button\u003e\n      {% endfor %}\n    \u003c/div\u003e\n  \u003c/div\u003e\n{% endfor %}\n```\n\n\n---\n\n## Trait drops\n\n### TraitDrop\n\nA product attribute/characteristic.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Trait name |\n| `value` | String | Trait value |\n| `trait_type` | TraitTypeDrop | Trait type definition |\n\n### TraitTypeDrop\n\nA trait type definition.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Type name |\n| `trait_group` | TraitGroupDrop | Parent group |\n\n### TraitGroupDrop\n\nA group of related traits.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Group name |\n| `trait_types` | Collection | Trait types in this group |\n\n### TraitCategoryDrop\n\nA category organizing trait groups.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | Category name |\n| `trait_groups` | Collection | Groups in this category |\n\n**Rendering traits:**\n\n\n```liquid\n\n{% for group in product.trait_groups %}\n  \u003ch4\u003e{{ group.name }}\u003c/h4\u003e\n  \u003cdl\u003e\n    {% for trait in product.traits %}\n      {% if trait.trait_type.trait_group.name == group.name %}\n        \u003cdt\u003e{{ trait.name }}\u003c/dt\u003e\n        \u003cdd\u003e{{ trait.value }}\u003c/dd\u003e\n      {% endif %}\n    {% endfor %}\n  \u003c/dl\u003e\n{% endfor %}\n```\n\n\nOr use the built-in filter for the default rendering:\n\n\n```liquid\n\n{{ current_product | show_traits }}\n```\n\n\n---\n\n## Common patterns\n\n### Accessing products\n\n\n```liquid\n\n{{ current_product.name }}\n\n{% for product in all_products %}\n  {{ product.name }}\n{% endfor %}\n\n{{ all_products.my-product-slug.name }}\n\n{% for product in current_product_category.products %}\n  {{ product.name }}\n{% endfor %}\n```\n\n\n### Product card\n\nA minimal product card covering image, brand, name, pricing, and availability:\n\n\n```liquid\n\n\u003cdiv class=\"product-card\"\u003e\n  {% if product.image %}\n    \u003ca href=\"{{ product.path }}\"\u003e\n      \u003cimg\n        src=\"{{ product.image.medium_url }}\"\n        srcset=\"{{ product.image.thumb_url }} 240w,\n                {{ product.image.medium_url }} 480w,\n                {{ product.image.large_url }} 640w\"\n        alt=\"{{ product.image.alt_text | default: product.name }}\"\n        loading=\"lazy\"\u003e\n    \u003c/a\u003e\n  {% endif %}\n\n  {% if product.brand %}\n    \u003cspan class=\"product-card__brand\"\u003e{{ product.brand.name }}\u003c/span\u003e\n  {% endif %}\n\n  \u003ch3 class=\"product-card__name\"\u003e\n    \u003ca href=\"{{ product.path }}\"\u003e{{ product.name }}\u003c/a\u003e\n  \u003c/h3\u003e\n\n  {% unless product.pricing.hide_price? %}\n    {% if product.pricing.on_sale? %}\n      \u003cspan class=\"product-card__price--original\"\u003e{{ product.pricing.original_price | money }}\u003c/span\u003e\n      \u003cspan class=\"product-card__price--sale\"\u003e{{ product.pricing.price | money }}\u003c/span\u003e\n    {% else %}\n      \u003cspan class=\"product-card__price\"\u003e{{ product.pricing.price | money }}\u003c/span\u003e\n    {% endif %}\n  {% else %}\n    \u003cspan class=\"product-card__price\"\u003e{{ product.pricing.hide_price_text }}\u003c/span\u003e\n  {% endunless %}\n\n  {% if product.out_of_stock? %}\n    \u003cspan class=\"badge badge--out-of-stock\"\u003e{{ product.out_of_stock_text | default: \"Out of stock\" }}\u003c/span\u003e\n  {% endif %}\n\u003c/div\u003e\n```\n\n\n### Image gallery\n\n\n```liquid\n\n{% if product.images.size \u003e 0 %}\n  \u003cdiv class=\"product-gallery\"\u003e\n    {% assign primary = product.images | first %}\n    \u003cdiv class=\"product-gallery__primary\"\u003e\n      \u003cimg src=\"{{ primary.large_url }}\" alt=\"{{ primary.alt_text | default: product.name }}\"\u003e\n    \u003c/div\u003e\n\n    {% if product.images.size \u003e 1 %}\n      \u003cdiv class=\"product-gallery__thumbnails\"\u003e\n        {% for image in product.images %}\n          \u003cimg\n            src=\"{{ image.thumb_url }}\"\n            data-large=\"{{ image.large_url }}\"\n            alt=\"{{ image.alt_text | default: product.name }}\"\n            loading=\"lazy\"\u003e\n        {% endfor %}\n      \u003c/div\u003e\n    {% endif %}\n  \u003c/div\u003e\n{% endif %}\n```\n\n\n### Add to cart\n\n\n```liquid\n\n{% if product.can_add_to_cart? %}\n  {% form \"add-to-cart\", product: product %}\n    {% if product.variant_types.size \u003e 0 %}\n      {% render \"products/variant_selector\", product: product %}\n    {% endif %}\n\n    \u003cinput type=\"number\"\n      name=\"{{ form.quantity.name }}\"\n      value=\"1\"\n      min=\"1\"\n      {% if product.maximum_quantity \u003e 0 %}max=\"{{ product.maximum_quantity }}\"{% endif %}\u003e\n\n    \u003cbutton type=\"submit\"\u003e{{ product.add_to_cart_text | default: \"Add to cart\" }}\u003c/button\u003e\n  {% endform %}\n{% elsif product.out_of_stock? %}\n  \u003cp\u003e{{ product.out_of_stock_text }}\u003c/p\u003e\n{% elsif product.restricted? %}\n  \u003cp\u003e{{ product.restricted_text }}\u003c/p\u003e\n{% endif %}\n```\n\n\n### Variant selector\n\nStoreConnect 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.\n\n```\nProduct (master)\n├── VariantType: \"Color\"  →  VariantOptions: Red, Blue, Green\n├── VariantType: \"Size\"   →  VariantOptions: Small, Medium, Large\n└── Variants (ProductDrops)\n    ├── \"Red / Small\"  ← VariantChoices link options to this variant\n    ├── \"Red / Medium\"\n    └── ...\n```\n\n\n```liquid\n\n{% if product.variant_types.size \u003e 0 %}\n  \u003cdiv class=\"variant-selector\" data-default-variant=\"{{ product.default_variant.id }}\"\u003e\n    {% for type in product.variant_types %}\n      \u003cdiv class=\"variant-selector__type\"\u003e\n        \u003clabel\u003e{{ type.name }}\u003c/label\u003e\n        \u003cdiv class=\"variant-selector__options\"\u003e\n          {% for option in type.options %}\n            \u003cbutton\n              class=\"variant-selector__option\"\n              data-variant-type=\"{{ type.name }}\"\n              data-variant-option=\"{{ option.name }}\"\u003e\n              {{ option.name }}\n            \u003c/button\u003e\n          {% endfor %}\n        \u003c/div\u003e\n      \u003c/div\u003e\n    {% endfor %}\n    \u003cinput type=\"hidden\" name=\"variant_id\" id=\"variant_id\"\u003e\n  \u003c/div\u003e\n{% endif %}\n```\n\n\n### Variant resolution in JavaScript\n\nThe add-to-cart form needs the selected variant's `id` in the `variant_id` field. The standard pattern uses JavaScript to:\n\n1. Capture the customer's selections (one option per variant type).\n2. Iterate through `product.variants` to find the variant whose `variant_choices` match all selections.\n3. Update the hidden `variant_id` field and display the matching variant's price and availability.\n\nTo expose variant data to JavaScript, serialize the variants in a `\u003cscript\u003e` tag:\n\n\n```liquid\n\n\u003cscript type=\"application/json\" id=\"product-variants\"\u003e\n  [\n    {% for variant in product.variants %}\n      {\n        \"id\": \"{{ variant.id }}\",\n        \"available\": {{ variant.can_add_to_cart? }},\n        \"price\": {{ variant.pricing.price }},\n        \"choices\": [\n          {% for choice in variant.variant_choices %}\n            {\n              \"type\": {{ choice.variant_option.variant_type.name | json }},\n              \"option\": {{ choice.variant_option.name | json }}\n            }{% unless forloop.last %},{% endunless %}\n          {% endfor %}\n        ]\n      }{% unless forloop.last %},{% endunless %}\n    {% endfor %}\n  ]\n\u003c/script\u003e\n```\n\n\n### Theme variables for variant selectors\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `product.variants.selector.buttons.maximum` | `5` | Max options shown as buttons before switching to a dropdown |\n\n### Trait rendering\n\nQuick rendering using the built-in filter:\n\n\n```liquid\n\n{{ product | show_traits }}\n```\n\n\nCustom rendering grouped by category and group:\n\n\n```liquid\n\n{% if product.trait_category %}\n  {% for group in product.trait_category.trait_groups %}\n    \u003csection\u003e\n      \u003ch4\u003e{{ group.name }}\u003c/h4\u003e\n      \u003cdl\u003e\n        {% for type in group.trait_types %}\n          {% for trait in product.traits %}\n            {% if trait.trait_type.name == type.name %}\n              \u003cdt\u003e{{ trait.name }}\u003c/dt\u003e\n              \u003cdd\u003e{{ trait.value }}\u003c/dd\u003e\n            {% endif %}\n          {% endfor %}\n        {% endfor %}\n      \u003c/dl\u003e\n    \u003c/section\u003e\n  {% endfor %}\n{% endif %}\n```\n\n\n### Related products\n\n\n```liquid\n\n{% if product.related_products.size \u003e 0 %}\n  \u003csection class=\"related-products\"\u003e\n    \u003ch2\u003eRelated products\u003c/h2\u003e\n    \u003cdiv class=\"product-grid\"\u003e\n      {% for related in product.related_products %}\n        {% render \"products/card\", product: related %}\n      {% endfor %}\n    \u003c/div\u003e\n  \u003c/section\u003e\n{% endif %}\n```\n\n\n### Product content sections\n\nRich content sections (features, specifications, support, warranty, downloads) are accessible as rendered HTML:\n\n\n```liquid\n\n{% if product.features_content %}\n  \u003csection\u003e\n    \u003ch2\u003e{{ product.features_label | default: \"Features\" }}\u003c/h2\u003e\n    {{ product.features_content }}\n  \u003c/section\u003e\n{% endif %}\n\n{% if product.specifications_content %}\n  \u003csection\u003e\n    \u003ch2\u003e{{ product.specifications_label | default: \"Specifications\" }}\u003c/h2\u003e\n    {{ product.specifications_content }}\n  \u003c/section\u003e\n{% endif %}\n```\n\n\nOr use the typed content block filters:\n\n\n```liquid\n\n{{ product | features_content_blocks }}\n{{ product | specifications_content_blocks }}\n{{ product | downloads_content_blocks }}\n```\n\n\n### Bundle products\n\n\n```liquid\n\n{% if product.is_bundle? %}\n  \u003cp\u003eThis product is a bundle\u003c/p\u003e\n{% endif %}\n\n{% if product.available_only_in_bundle? %}\n  \u003cp\u003eAvailable only as part of a bundle\u003c/p\u003e\n  {% for bundle in product.contained_in_bundles %}\n    \u003ca href=\"{{ bundle.path }}\"\u003e{{ bundle.name }}\u003c/a\u003e\n  {% endfor %}\n{% endif %}\n```\n"}