Skip to content

collection

The collection object represents a published storefront collection and its current product view. The view can be narrowed by filters, reordered by a sort option, and paginated without exposing seller-only catalog data.

Availability and context

On a collection template, collection is the collection being viewed. A collection can also be reached through a product’s collections property, menus, resource settings, and collection lookup objects. It can be nil when a handle is missing, invalid, or unpublished.

Counts, tags, filters, and products are contextual. For example, products_count describes the active filtered view, while all_products_count describes the collection before current-view filtering.

Examples

Paginate a product grid

<h1>{{ collection.title | escape }}</h1>
{% paginate collection.products by 24 %}
<ul>
{% for product in collection.products %}
<li>
<a href="{{ product.url }}">{{ product.title | escape }}</a>
<span>{{ product.price | money }}</span>
</li>
{% else %}
<li>No products match this view.</li>
{% endfor %}
</ul>
{% if paginate.pages > 1 %}
{{ paginate | default_pagination }}
{% endif %}
{% endpaginate %}

Example output begins:

<h1>Home fragrance</h1>
<ul>
<li><a href="/products/sandalwood-candle">Sandalwood Candle</a><span>₹1,499.00</span></li>
<li><a href="/products/cedar-diffuser">Cedar Diffuser</a><span>₹1,999.00</span></li>
</ul>

Explain active and total counts

<p>
Showing {{ collection.products_count }} of
{{ collection.all_products_count }} products
</p>
{% if collection.current_tags != empty %}
<p>Tags: {{ collection.current_tags | join: ', ' | escape }}</p>
{% endif %}

Example output:

<p>Showing 8 of 42 products</p>
<p>Tags: Gift, Soy</p>

collection.tags follows the active filtered or search view and is nil when no applicable tag exists. all_tags describes the unfiltered collection and is empty when that collection has no tags.

Build a sort selector

{% assign active_sort = collection.sort_by | default: collection.default_sort_by %}
<form method="get" action="{{ collection.url }}">
<label for="SortBy">Sort by</label>
<select id="SortBy" name="sort_by" onchange="this.form.submit()">
{% for option in collection.sort_options %}
<option value="{{ option.value }}"{% if option.value == active_sort %} selected{% endif %}>
{{ option.name | escape }}
</option>
{% endfor %}
</select>
</form>

Example output:

<form method="get" action="/collections/candles">
<label for="SortBy">Sort by</label>
<select id="SortBy" name="sort_by">
<option value="manual">Featured</option>
<option value="best-selling" selected>Best selling</option>
<option value="title-ascending">Alphabetically, A–Z</option>
<option value="title-descending">Alphabetically, Z–A</option>
<option value="price-ascending">Price, low to high</option>
<option value="price-descending">Price, high to low</option>
<option value="created-descending">Date, new to old</option>
<option value="created-ascending">Date, old to new</option>
<option value="collection-added-descending">Date added, new to old</option>
<option value="collection-added-ascending">Date added, old to new</option>
</select>
</form>

sort_by is nil until the buyer explicitly chooses a sort. Use default_sort_by as the fallback.

Render list filters without losing canonical URLs

<form method="get" action="{{ collection.url }}">
{% for filter in collection.filters %}
{% if filter.type == 'list' or filter.type == 'boolean' %}
<fieldset>
<legend>{{ filter.label | escape }}</legend>
{% for value in filter.values %}
<label>
<input
type="checkbox"
name="{{ value.param_name }}"
value="{{ value.value | escape }}"
{% if value.active %}checked{% endif %}
{% if value.count == 0 and value.active == false %}disabled{% endif %}>
{{ value.label | escape }} ({{ value.count }})
</label>
{% endfor %}
</fieldset>
{% endif %}
{% endfor %}
<button type="submit">Apply</button>
</form>

Example output:

<fieldset>
<legend>Color</legend>
<label><input type="checkbox" name="filter.v.option.color" value="Red" checked> Red (8)</label>
<label><input type="checkbox" name="filter.v.option.color" value="Blue"> Blue (5)</label>
</fieldset>

Filtered products retain their relevant variant context: product URLs, featured media, and variant.matched stay aligned with the buyer’s active filters.

Nil values, ordering, and limits

  • Iterating collection.products without paginate exposes the first 50 products. Pagination has a maximum page size of 250 and can navigate only the first 25,000 products in the result set.
  • Counts larger than the exact storefront-count limit use the 25001 sentinel. Treat it as “more than 25,000,” not as an exact total.
  • collection.filters is empty whenever the base collection contains more than 5,000 unfiltered products, even when the buyer’s current filters narrow the view below that threshold. Themes must remain usable when filters are unavailable.
  • Collection tag, merchant-type, brand, and vendor aggregates contain at most 1,000 distinct values.
  • featured_image falls back from the collection image to legacy featured media and then to the first product image; it is nil if none exists.
  • sort_by, current_tag, previous_product, and template_suffix can be nil. Despite their compatibility names, next_product currently returns the first product in the current collection view and previous_product returns the last product when the view contains at least two products; they are not relative to an implicit current product.
  • Product and filter order is authoritative storefront order. Do not re-sort filter values unless your design deliberately overrides the seller’s filter configuration.

Collection drops and nested products are public storefront projections. Unpublished collections, products, filter values, and seller-only catalog fields are not made available through Liquid or | json.

Properties

PropertyDescription
collection.all_brandsUp to 1,000 brands represented before current-view filtering.
collection.all_products_countTotal products before current-view filtering, capped by the storefront count sentinel.
collection.all_tagsUp to 1,000 tags across products before current-view filtering.
collection.all_typesAlias of all_user_product_types; up to 1,000 merchant-defined product types.
collection.all_user_product_typesUp to 1,000 merchant-defined product types before current-view filtering.
collection.all_vendorsAlias of all_brands; up to 1,000 vendor or brand names.
collection.current_brandActive legacy brand/vendor view, or nil.
collection.current_tagFirst active legacy tag, or nil.
collection.current_tagsActive legacy tag values.
collection.current_typeActive legacy product-type view, or nil.
collection.current_user_product_typeActive merchant-defined product type, or nil.
collection.current_vendorActive legacy vendor view, or nil.
collection.default_sort_byDefault sort key.
collection.descriptionDescription (HTML).
collection.featured_imageThe featured image.
collection.filtersStorefront filters with values and active state; empty when the base unfiltered collection exceeds 5,000 products.
collection.handleURL-safe unique slug.
collection.idUnique identifier.
collection.imagePrimary collection image, or nil.
collection.metafieldsMetafields on this resource.
collection.next_productFirst product in the current collection view, or nil when the view is empty. It is not relative to a current product.
collection.previous_productLast product in the current collection view when at least two products exist; otherwise nil. It is not relative to a current product.
collection.productsProducts in the current view: first 50 without pagination, page size up to 250, and at most the first 25,000 navigable through pagination.
collection.products_countProducts in the current view, using 25001 as the “more than 25,000” sentinel.
collection.published_atPublish timestamp.
collection.seo_descriptionCollection SEO description.
collection.seo_titleCollection SEO title.
collection.sort_byActive sort key.
collection.sort_optionsThe ten supported storefront sort options in authoritative display order.
collection.tagsUp to 1,000 tags for the active collection/search/filter view, or nil when none apply.
collection.template_suffixAssigned alternate template suffix, or nil when the default template is used.
collection.titleDisplay title.
collection.urlRelative URL of the resource.

Property names are generated from the storefront engine (CollectionDrop), so the list matches what themes can access. {{ collection | json }} is only the curated predictive-resource subset (id, title, handle, url, image, and featured_image). Its serialized image fields use embedded collection media and do not run the live featured_image first-product fallback; inspect other properties directly.

Sellerlane differences from Shopify

  • Canonical tag filtering uses filter.p.tag. current_tag and current_tags remain compatibility views derived from that filter instead of defining a separate legacy tag URL system.
  • Sellerlane supports its public filter namespace and configured storefront filter manifest. Sellerlane never emits Shopify standard-attribute parameters such as filter.v.t.shopify.*; a trusted non-empty manifest excludes an unconfigured key. The current manifest-free compatibility parser can treat an arbitrary filter.v.* spelling as a variant-option filter, so themes must not construct or rely on that fallback.
  • Relevant variant matching is authoritative backend state. Themes should use the supplied product URL, featured media, and variant.matched instead of reconstructing matches in Liquid.
  • Shopify B2B, Markets, and app-provided filter behavior are outside Sellerlane’s compatibility target.