Liquid context reference
This is the complete set of objects your theme can read. The platform builds
this context and hands it to your Liquid - you never fetch data yourself. A
machine-readable version is available at
/context-schema.json (see the
API reference).
Global objects
Available in every template and section.
| Variable | Notes |
|---|---|
shop | Storefront-wide info (see below). |
settings | Merchant’s global theme settings, keyed by your settings_schema.json ids. |
cart | Always present, may be empty. |
customer | The signed-in customer, or null. |
linklists | Menus keyed by handle. |
collections | Iterable list AND a handle-keyed map of all collections. |
request | { path, page_type, locale, host }. |
template | { name } - alias for request.page_type. |
routes | Canonical URLs for cart / search / account routes. |
canonical_url | Full URL of the current page. |
Per-page extras
- Product page -
product - Collection page -
collection(withproducts[]+facets),paginate,active_filters - Search page -
search,active_filters - Content page -
page
shop
Storefront-wide information.
| Field | Type | Notes |
|---|---|---|
name | string | Store / business name. |
currency | string | ISO currency code (e.g. INR). |
slug | string | Store brand slug. |
primary_domain | string | Primary storefront domain. |
description | string | null | |
tagline | string | null | |
logo | string | null | Logo image URL. |
seo_title | string | null | <title> override. |
seo_description | string | null | <meta description> override. |
og_image | string | null | |
favicon | string | null | Null until a logo is uploaded. |
apple_touch_icon | string | null | |
web_manifest | string | null | |
cod_enabled | boolean | Cash-on-delivery available. Drives trust-strip copy. |
free_shipping_threshold | number | Free-shipping order value; 0 = none. |
delivery_check_enabled | boolean | Serviceability/pincode check available. Gate the PDP pincode checker on this. |
enabled_payment_types | string[] | Accepted payment methods. The single source of truth for the payment-icon row. |
payment_icons_html | string | Pre-rendered payment-icon row - output verbatim behind your own show_payment_icons toggle. |
settings
The merchant’s global theme settings, keyed by the ids you declare in
config/settings_schema.json. Access as {{ settings.color_primary }},
{{ settings.logo }}, and so on. See
Sections & settings.
Some well-known ids (color_bg, color_primary, color_surface, color_fg,
color_border, color_accent, font_body_family, font_heading_family) also
brand the merchant’s cart / account pages when your theme is live - use these
exact ids to opt in for free.
product
Available on the product page, and nested inside collection.products[] and
search.results[].
| Field | Type | Notes |
|---|---|---|
id, title, handle | string | |
description, excerpt | string | null | |
url | string | |
type | string | |
vendor | string | null | Brand/manufacturer. Guard with {% if product.vendor %}. |
tags | string[] | |
images | { url, alt_text, position }[] | |
featured_image | string | null | First image URL. |
available | boolean | |
price | number | Lowest variant price, in paise. |
compare_at_price | number | null | In paise. |
variants | variant[] | See below. |
options | product_option[] | Use this for swatches / size pickers. |
collections | { id, title, handle }[] | |
rating | number | Average 0-5. |
rating_count | number | |
rating_breakdown | number[] | 5-cell histogram; index 0 = 1-star. |
reviews | array | { id, rating, title, body, images[], verified, reviewer_name, created_at } |
is_featured | boolean | |
custom_fields | custom_field[] | Iterate for PDP accordion panels. |
custom_fields_by_key | map | Keyed accessor: product.custom_fields_by_key.fabric_care. |
related_products | product[] | “You may also like” rail. |
seo_title, seo_description | string | null |
product.options[] and variants
Every product surfaces a structured options[] array. Use it for swatches and
size pickers - never split variant.title on /.
Each option group:
| Field | Type | Notes |
|---|---|---|
id, name | string | "Color", "Size", … |
kind | string | SIZE | COLOR | CAPACITY | MATERIAL | FRAME | WEIGHT | PACK_SIZE | FRAGRANCE | STYLE | CUSTOM. Branch on this, not name. |
system | string | null | null = freeform; a system id when canonical. |
position | number | 1, 2, or 3. |
values | array | See below. |
Each option value:
| Field | Type | Notes |
|---|---|---|
id, label | string | "M", "Onyx", "27". |
sort_order | number | |
swatch_hex | string | null | Resolved hex: override → canonical → null. |
canonical | object | null | { id, system, label, regional_labels, body_measurements, swatch_hex } when linked to the taxonomy. |
Each variant:
| Field | Type | Notes |
|---|---|---|
id, title, sku | string | |
price | number | In paise. |
compare_at_price | number | null | In paise. |
available | boolean | Active AND in stock. |
inventory_quantity | number | |
images | string[] | |
option_values | array | { option_id, option_name, option_kind, option_position, value_id, label, swatch_hex, canonical } per option group. |
Rendering option pickers
Branch on option.kind:
{% for option in product.options %}
{% if option.kind == 'COLOR' %}
{% for v in option.values %}
<button class="swatch" style="--swatch: {{ v.swatch_hex | default: '#ccc' }};"
data-option-value-id="{{ v.id }}" aria-label="{{ v.label }}"></button>
{% endfor %}
{% elsif option.kind == 'SIZE' %}
{% for v in option.values %}
<button class="chip" data-option-value-id="{{ v.id }}">{{ v.label }}</button>
{% endfor %}
{% endif %}
{% endfor %}product.custom_fields[]
Merchant-defined typed metafields. Two access patterns:
{# iterate every attached field #}
{% for f in product.custom_fields %}
<h3>{{ f.label }}</h3>
{% if f.type == 'RICH_TEXT' %}{{ f.value_text }}
{% elsif f.type == 'IMAGE' %}<img src="{{ f.value_image }}" alt="{{ f.label }}">
{% elsif f.type == 'IMAGE_LIST' %}{% for img in f.value_images %}<img src="{{ img }}">{% endfor %}
{% elsif f.type == 'NUMBER' %}{{ f.value_number }}
{% else %}{{ f.value_text }}{% endif %}
{% endfor %}
{# look up a specific field by key #}
{% assign care = product.custom_fields_by_key.fabric_care %}
{% if care %}<div class="rte">{{ care.value_text }}</div>{% endif %}Each field: { id, key, label, type, description, position, value, value_text, value_image, value_images, value_number, value_json }. type is one of
TEXT | RICH_TEXT | IMAGE | IMAGE_LIST | VIDEO | URL | NUMBER | JSON_LIST.
collection
Available on collection pages, and every entry in the global collections list.
| Field | Type | Notes |
|---|---|---|
id, title, handle | string | |
description | string | null | |
image | string | null | |
url | string | |
products_count | number | Use for “N products” labels. |
show_in_nav | boolean | |
position | number | |
parent_id | string | null | Filter top nav by parent_id == blank. |
children | collection[] | Sub-collections (populated on the global map). |
products | product[] | Paginated products. |
facets | object | null | Filter-sidebar source. |
seo_title, seo_description | string | null |
On the global
collectionslist, each entry carries metadata only -.productsis[]unless a section/block on the current page references that collection, or it is the page’s own collection. Useproducts_countfor counts on collection-list tiles.
collection.facets
The canonical source for the filter sidebar. Read from this - do not compute
facets client-side from products[].
facets: {
sizes: [{ label, hex, count }], // hex always null for sizes
colors: [{ label, hex, count }], // hex = override || canonical || null
priceRange: { min, max }, // in RUPEES (not paise)
categories: [{ label, count }]
}paginate
{ current_page, pages, items, page_size, next, previous }active_filters
Parsed active filter state - read from this rather than parsing the query string:
{ sortBy, sizes[], colors[], minPrice, maxPrice, inStockOnly, onSaleOnly,
categoryHandles[], minRating, q }minPrice / maxPrice are in RUPEES (render raw as ₹{{ active_filters.maxPrice }}).
Filter forms must submit as GET so the URL is shareable. Recognised query
params: size, color, min_price, max_price, on_sale, in_stock,
category, min_rating, sort_by, page.
cart
Always present. All prices in paise - render with the money filter.
| Field | Type |
|---|---|
id | string |
item_count | number |
items | cart_item[] |
total_price, items_subtotal, items_subtotal_price | number |
tax_total | number |
tax_lines | { name, rate, amount }[] |
discount_total, grand_total | number |
applied_discount_codes | string[] |
note | string | null |
Each line item: { id, product_id, variant_id, quantity, title, variant_title, sku, url, image, price, final_price, original_price, unit_price, line_price, final_line_price, original_line_price }.
{{ item.line_price | money }}linklists
Navigation menus keyed by handle. Your theme must iterate
linklists['main-menu'].links for the header and
linklists['footer-menu'].links for the footer. Both handles are
platform-guaranteed to resolve. See Navigation.
{% for link in linklists['main-menu'].links %}
<a href="{{ link.url }}">{{ link.title }}</a>
{% if link.links.size > 0 %}
<ul class="dropdown">
{% for child in link.links %}
<li><a href="{{ child.url }}">{{ child.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}Each link: { title, url, type, resource, links[] }. Child links live on
link.links (not link.children). type is one of
COLLECTION | PRODUCT | PAGE | URL | CATALOG.
customer
null when signed out. When signed in:
{ id, name, first_name, email, phone, logged_in }search
On the /search page:
{ terms, performed, results: product[], results_count }performed is true when a non-empty query ran. Render a styled empty state
when results_count == 0.
page
On a content page (templates/page.json):
{ title, handle, content }content is rich-text body HTML. Render {{ page.content }}. The
platform-seeded legal pages (About us, Privacy Policy, Terms of Service, Cookie
Policy) rely on this.
request, template, routes
request = { path, page_type, locale, host }
template = { name } // alias for request.page_type
routes = { root, cart_url, search_url, collections_url, account_url, account_login_url }Use routes.* for account-app links rather than hardcoding paths.