Skip to content

Examples

TIP

These examples run against a Shopify storefront using the Liquid July '26 partial rendering preview, so they are shown as code rather than live playgrounds. See the Fetch examples for runnable demonstrations of the inherited behaviour.

Sorting a collection

Refresh the product grid and the results count when the customer picks a sort order, keeping their scroll position.

liquid
{% partial 'product-grid' %}
  <ul id="product-grid" class="grid grid-cols-2 gap-4 md:grid-cols-4">
    {% for product in collection.products %}
      {% render 'product-card', product: product %}
    {% endfor %}
  </ul>
{% endpartial %}

{% partial 'product-count' %}
  <p id="product-count">{{ collection.products_count }} products</p>
{% endpartial %}

<a
  href="{{ collection.url }}?sort_by=price-ascending"
  data-component="FetchShopifyPartial"
  data-option-partials="product-grid,product-count"
  data-option-history>
  Sort by price
</a>
ts
import { registerComponent } from '@studiometa/js-toolkit';
import { FetchShopifyPartial } from '@studiometa/ui';

registerComponent(FetchShopifyPartial);

Faceted filtering with a form

Use a <form method="get"> so the selected facets are appended to the URL automatically, and display a loader while the partials are refreshed with the inherited Action and Transition components.

liquid
<form
  action="{{ collection.url }}"
  method="get"
  data-component="FetchShopifyPartial Action"
  data-option-partials="product-grid,active-filters"
  data-on:change="$el.requestSubmit()"
  data-on:fetch-before="Transition(#filters-loader) -> target.enter()"
  data-on:fetch-after="Transition(#filters-loader) -> target.leave()">
  {% for filter in collection.filters %}
    {% render 'facet', filter: filter %}
  {% endfor %}
  <noscript><button type="submit">Apply</button></noscript>
</form>

<div
  id="filters-loader"
  data-component="Transition"
  data-option-enter-from="opacity-0"
  data-option-enter-active="transition"
  data-option-leave-to="opacity-0"
  data-option-leave-active="transition"
  data-option-leave-keep
  class="transition opacity-0">
  Loading…
</div>

{% partial 'active-filters' %}
  <div id="active-filters">{% render 'active-filters' %}</div>
{% endpartial %}

{% partial 'product-grid' %}
  <ul id="product-grid">{% render 'product-list', products: collection.products %}</ul>
{% endpartial %}
ts
import { registerComponent } from '@studiometa/js-toolkit';
import { Action, FetchShopifyPartial, Transition } from '@studiometa/ui';

registerComponent(Action);
registerComponent(FetchShopifyPartial);
registerComponent(Transition);

TIP

Changing a facet fires a native change event that bubbles to the form, where the Action binding calls requestSubmit() to trigger the request — no submit button is required once JavaScript runs. Keep the <noscript> submit button so filtering still works without JavaScript.

Reacting to the update event

FetchShopifyPartial inherits the Fetch events. Use the fetch-update-after event to run logic once the regions have been swapped — for example scrolling the refreshed grid back into view after paginating. As with the base component, events bubble, so the Action handler can live on a parent element.

liquid
<div
  data-component="Action"
  data-on:fetch-update-after="document.getElementById('product-grid').scrollIntoView()">
  <a
    href="{{ collection.url }}?page=2"
    data-component="FetchShopifyPartial"
    data-option-partials="product-grid">
    Next page
  </a>
</div>
ts
import { registerComponent } from '@studiometa/js-toolkit';
import { Action, FetchShopifyPartial } from '@studiometa/ui';

registerComponent(Action);
registerComponent(FetchShopifyPartial);