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.productswithoutpaginateexposes 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
25001sentinel. Treat it as “more than 25,000,” not as an exact total. collection.filtersis 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_imagefalls back from the collection image to legacy featured media and then to the first product image; it isnilif none exists.sort_by,current_tag,previous_product, andtemplate_suffixcan benil. Despite their compatibility names,next_productcurrently returns the first product in the current collection view andprevious_productreturns 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
| Property | Description |
|---|---|
collection.all_brands | Up to 1,000 brands represented before current-view filtering. |
collection.all_products_count | Total products before current-view filtering, capped by the storefront count sentinel. |
collection.all_tags | Up to 1,000 tags across products before current-view filtering. |
collection.all_types | Alias of all_user_product_types; up to 1,000 merchant-defined product types. |
collection.all_user_product_types | Up to 1,000 merchant-defined product types before current-view filtering. |
collection.all_vendors | Alias of all_brands; up to 1,000 vendor or brand names. |
collection.current_brand | Active legacy brand/vendor view, or nil. |
collection.current_tag | First active legacy tag, or nil. |
collection.current_tags | Active legacy tag values. |
collection.current_type | Active legacy product-type view, or nil. |
collection.current_user_product_type | Active merchant-defined product type, or nil. |
collection.current_vendor | Active legacy vendor view, or nil. |
collection.default_sort_by | Default sort key. |
collection.description | Description (HTML). |
collection.featured_image | The featured image. |
collection.filters | Storefront filters with values and active state; empty when the base unfiltered collection exceeds 5,000 products. |
collection.handle | URL-safe unique slug. |
collection.id | Unique identifier. |
collection.image | Primary collection image, or nil. |
collection.metafields | Metafields on this resource. |
collection.next_product | First product in the current collection view, or nil when the view is empty. It is not relative to a current product. |
collection.previous_product | Last product in the current collection view when at least two products exist; otherwise nil. It is not relative to a current product. |
collection.products | Products 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_count | Products in the current view, using 25001 as the “more than 25,000” sentinel. |
collection.published_at | Publish timestamp. |
collection.seo_description | Collection SEO description. |
collection.seo_title | Collection SEO title. |
collection.sort_by | Active sort key. |
collection.sort_options | The ten supported storefront sort options in authoritative display order. |
collection.tags | Up to 1,000 tags for the active collection/search/filter view, or nil when none apply. |
collection.template_suffix | Assigned alternate template suffix, or nil when the default template is used. |
collection.title | Display title. |
collection.url | Relative 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_tagandcurrent_tagsremain 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 arbitraryfilter.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.matchedinstead of reconstructing matches in Liquid. - Shopify B2B, Markets, and app-provided filter behavior are outside Sellerlane’s compatibility target.