Skip to main content

Home Sections

Home sections render on the store's homepage. They display the hero slider, category navigation, and product collections in different layouts.


Slider

File: sections/slider.liquid

A hero image slider / carousel. Each slide uses image as the main source and can optionally use mobile_image on small screens.

Variables

VariableTypeDescription
slidesarraySlide objects (see properties below)
theme_dataobjectMerchant-configured dynamic settings

Slide properties:

PropertyTypeDescription
slide.imagestringMain image URL (desktop + tablet, and fallback for all sizes)
slide.mobile_imagestring | nullOptional mobile image URL used on screens <= 425px
slide.urlstring | nullOptional link destination
slide.altstringAlt text for the image
info

Render an <a> element only when slide.url is not empty; otherwise render a non-clickable <div>. Use a <picture> element to serve slide.mobile_image on small screens and slide.image for the rest. See example for more details.

Example

<section class="slider">
<div class="slider-track">
{% for slide in slides %}
{% if slide.url != blank %}
<a href="{{ slide.url }}" class="slide{% if forloop.first %} active{% endif %}">
<picture>
{% if slide.mobile_image != blank %}
<source media="(max-width: 425px)" srcset="{{ slide.mobile_image }}" />
<source media="(min-width: 426px)" srcset="{{ slide.image }}" />
{% endif %}
<img
src="{{ slide.image }}"
alt="{{ slide.alt | default: "Slide image" }}"
loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
/>
</picture>
</a>
{% else %}
<div class="slide{% if forloop.first %} active{% endif %}">
<picture>
{% if slide.mobile_image != blank %}
<source media="(max-width: 425px)" srcset="{{ slide.mobile_image }}" />
<source media="(min-width: 426px)" srcset="{{ slide.image }}" />
{% endif %}
<img
src="{{ slide.image }}"
alt="{{ slide.alt | default: "Slide image" }}"
loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
/>
</picture>
</div>
{% endif %}
{% endfor %}
</div>
</section>
tip

Use loading="eager" on the first slide and loading="lazy" on the rest for optimal performance. The <picture> element with <source> lets you serve different images for mobile and desktop.


Categories

File: sections/categories.liquid

A category grid or carousel that links to collection pages.

Variables

VariableTypeDescription
categoriesarrayCategory objects (see properties below)
theme_dataobjectMerchant-configured dynamic settings

Category properties:

PropertyTypeDescription
category.namestringCategory display name
category.slugstringURL slug
category.thumbstringCategory thumbnail image URL

Example

<div class="categories">
{% for category in categories %}
<a href="/collections/{{ category.slug }}" class="category-card">
<img src="{{ category.thumb }}" alt="{{ category.name }}" loading="lazy" />
<span>{{ category.name }}</span>
</a>
{% endfor %}
</div>

Three section files share the same variable contract but render products in different layouts on the homepage:

FileLayoutTypical Use
sections/featured-products.liquidFeatured hero + card gridHomepage featured collection
sections/list-products.liquidHorizontal scrollable listHomepage product carousel
sections/home-products-grid.liquidMulti-column grid with heroHomepage product grid

Variables (shared)

VariableTypeDescription
productsarrayProduct objects (see properties below)
categoryobject | nullParent category with name, slug, thumb
section_titlestringSection heading text
currencystringCurrency symbol/code
addstringTranslated "Add" button text
shop_nowstringTranslated "Shop now" text
salestringTranslated "Sale" label
hide_view_allbooleanWhether to hide the "View all" link
theme_dataobjectMerchant-configured dynamic settings

Product properties:

PropertyTypeDescription
product.idstringProduct ID (used in events)
product.namestringProduct name
product.slugstringURL slug
product.pricenumberRegular price
product.sale_pricenumber | nullSale price
product.thumbstringMain thumbnail URL
product.imagesstring[]Additional image URLs
product.variationsarrayVariation groups with type and props[]

Variation properties (for color swatches):

PropertyTypeDescription
variation.typestring"color" or "image"
variation.propsarraySwatch items with name and value

Events

EventDetailPurpose
quick-add{ productId: string }Adds the product to cart directly
quick-view{ productId: string }Opens a quick-view modal for the product
toggle-wishlist{ productId: string }Toggles the product in the wishlist (adds if absent, removes if already saved)
toggle-compare{ productId: string }Adds the product to the compare list (if not present) and opens the compare modal

Dispatch from your add-to-cart button:

onclick="event.preventDefault();event.stopPropagation();this.dispatchEvent(new
CustomEvent('quick-add',{bubbles:true,detail:{productId:'{{ product.id }}'}}))"

Dispatch from your wishlist / compare buttons:

<button type="button"
onclick="event.preventDefault();event.stopPropagation();
this.dispatchEvent(new CustomEvent('toggle-wishlist',{bubbles:true,detail:{productId:'{{ product.id }}'}}))">

</button>

<button type="button"
onclick="event.preventDefault();event.stopPropagation();
this.dispatchEvent(new CustomEvent('toggle-compare',{bubbles:true,detail:{productId:'{{ product.id }}'}}))">
Compare
</button>
<section class="featured">
{% if section_title %}
<h2>{{ section_title }}</h2>
{% endif %}

<div class="featured-grid">
{% for product in products limit: 4 %}
<a href="/products/{{ product.slug }}" class="product-card">
{% if product.sale_price and product.sale_price < product.price %}
{% assign discount = product.price | minus: product.sale_price | times: 100 | divided_by: product.price | floor %}
<span class="badge">{{ sale }} -{{ discount }}%</span>
{% endif %}

<div class="product-media">
<img src="{{ product.thumb }}" alt="{{ product.name }}" loading="lazy" />
{% if product.images[0] %}
<img class="hover-img" src="{{ product.images[0] }}" alt="{{ product.name }}" loading="lazy" />
{% endif %}
</div>

<div class="product-info">
<p>{{ product.name }}</p>
{% if product.sale_price and product.sale_price < product.price %}
<span class="price-old">{{ product.price }} {{ currency }}</span>
<span class="price-sale">{{ product.sale_price }} {{ currency }}</span>
{% else %}
<span>{{ product.price }} {{ currency }}</span>
{% endif %}
</div>

<button type="button"
onclick="event.preventDefault();event.stopPropagation();this.dispatchEvent(new CustomEvent('quick-add',{bubbles:true,detail:{productId:'{{ product.id }}'}}));">
{{ add }}
</button>
</a>
{% endfor %}
</div>
</section>

Color Swatches Pattern

All three product layouts can display color/image swatches from the product variations:

{% assign color_variation = product.variations | where: "type", "color" | first %}
{% assign image_variation = product.variations | where: "type", "image" | first %}

{% if color_variation and color_variation.props %}
{% for prop in color_variation.props limit: 4 %}
<span class="swatch" style="background: {{ prop.value }}" title="{{ prop.name }}"></span>
{% endfor %}
{% elsif image_variation and image_variation.props %}
{% for prop in image_variation.props limit: 4 %}
<span class="swatch swatch-image" title="{{ prop.name }}">
<img src="{{ prop.value }}" alt="{{ prop.name }}" loading="lazy" />
</span>
{% endfor %}
{% endif %}

Discount Badge Pattern

Calculate and display a percentage discount:

{% if product.sale_price and product.sale_price < product.price %}
{% assign discount = product.price | minus: product.sale_price | times: 100 | divided_by: product.price | floor %}
<span class="badge">{{ sale }} -{{ discount }}%</span>
{% endif %}

Products Grid (Search & Collections pages)

File: sections/products-grid.liquid
Section key: products_grid

A product grid rendered on the search page and collection pages (browsing a category). Unlike the homepage grid (home-products-grid.liquid), this template has no hero block and renders all products received from the current page of results. The "load more" button is rendered outside the Liquid template by the storefront.

Variables (shared)

The same product variable contract as the homepage product sections applies here:

VariableTypeDescription
productsarrayCurrent page of product objects
categoryobject | nullCurrent category with name, slug, thumb (collections page only; null on search)
section_titlestringSection heading — category name on collections, search query on search page
currencystringCurrency symbol/code
addstringTranslated "Add" button text
shop_nowstringTranslated "Shop now" text
salestringTranslated "Sale" label
hide_view_allbooleanAlways true on this section (storefront hides the view-all link)
theme_dataobjectMerchant-configured dynamic settings

Product properties and variation properties are identical to the other product sections — see above.

Events

EventDetailPurpose
quick-add{ productId: string }Adds the product to cart directly
quick-view{ productId: string }Opens a quick-view modal for the product
toggle-wishlist{ productId: string }Toggles the product in the wishlist (adds if absent, removes if already saved)
toggle-compare{ productId: string }Adds the product to the compare list (if not present) and opens the compare modal

Example

<section class="products-grid">
{% if section_title %}
<h2 class="products-grid-title">{{ section_title }}</h2>
{% endif %}

<div class="products-grid-wrap">
{% for product in products %}
<a href="/products/{{ product.slug }}" class="product-card">
{% if product.sale_price and product.sale_price < product.price %}
{% assign discount = product.price | minus: product.sale_price | times: 100 | divided_by: product.price | floor %}
<span class="badge">{{ sale }} -{{ discount }}%</span>
{% endif %}

<div class="product-media">
<img src="{{ product.thumb }}" alt="{{ product.name }}" loading="lazy" />
{% if product.images[0] %}
<img class="hover-img" src="{{ product.images[0] }}" alt="{{ product.name }}" loading="lazy" />
{% endif %}
<button type="button"
onclick="event.preventDefault();event.stopPropagation();this.dispatchEvent(new CustomEvent('quick-add',{bubbles:true,detail:{productId:'{{ product.id }}'}}));">
{{ add }}
</button>
</div>

<div class="product-info">
<p>{{ product.name }}</p>
{% if product.sale_price and product.sale_price < product.price %}
<span class="price-old">{{ product.price }} {{ currency }}</span>
<span class="price-sale">{{ product.sale_price }} {{ currency }}</span>
{% else %}
<span>{{ product.price }} {{ currency }}</span>
{% endif %}
</div>
</a>
{% endfor %}
</div>
</section>
info

On collections pages the storefront renders a "Load more" button outside this template using React. Each click fetches the next page and re-renders this template with the full accumulated product list.