Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.uselayers.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This is the SDK equivalent of Rendering sort orders in Liquid. You still render the dropdown from sort metaobjects in Liquid — that’s the only way to keep the UI in sync with the dashboard — but you pass the selected code to the SDK’s client.collection() or client.search() controller as sort instead of issuing the API request yourself. Use this version when you’ve already installed @commerce-blocks/sdk in your theme. For a no-dependencies version, see the Liquid + Fetch guide.

Prerequisites

  • Sort orders configured in the Layers dashboard with Enable as Storefront Sort turned on.
  • The SDK installed and a client created in your theme — see SDK installation. Mirror the dashboard sort orders into the sorts config (the snippet in Rendering facets with the SDK generates it from metaobjects).

Collection sort dropdown

1

Render the dropdown from sort metaobjects

Filter by the collection scope and order by the configured order field. Read the collection’s default sort from layers.default_sort_order and the active sort from the URL.
{% assign layers_sorts = shop.metaobjects['app--278936322049--sort_order'] | sort: 'order' %}

{% assign default_sort_code = nil %}
{% if collection.metafields.layers.default_sort_order %}
  {% assign default_sort_code = collection.metafields.layers.default_sort_order.value.code.value %}
{% endif %}

{% assign active_sort_code = request.params.sort | default: default_sort_code %}

<label for="sort-order">Sort by</label>
<select
  id="sort-order"
  name="sort"
  data-collection="{{ collection.handle }}"
  data-default-sort="{{ default_sort_code }}"
>
  {% for sort in layers_sorts %}
    {% assign scopes = sort.scope.value %}
    {% if scopes contains 'collection' %}
      <option
        value="{{ sort.code.value }}"
        {% if sort.code.value == active_sort_code %}selected{% endif %}
      >
        {{ sort.name.value }}
      </option>
    {% endif %}
  {% endfor %}
</select>
2

Drive a collection controller from the dropdown

Create the controller once, subscribe to render, and call execute({ sort }) whenever the selection changes. The SDK dedupes against in-flight requests and updates the state signal — subscribers re-render automatically.
const select = document.querySelector('#sort-order')
const collection = window.layers.collection({
  handle: select.dataset.collection,
  defaultSort: select.dataset.defaultSort || undefined,
})

collection.subscribe(({ data, isFetching }) => {
  if (isFetching) showLoading()
  if (data) renderGrid(data.products)
})

// Initial load — uses the option pre-selected by Liquid
await collection.execute({ sort: select.value || undefined })

select.addEventListener('change', (event) => {
  const sortCode = event.target.value || undefined
  collection.execute({ sort: sortCode })

  // Mirror selection to the URL so it survives refresh and back navigation
  const url = new URL(window.location.href)
  if (sortCode) url.searchParams.set('sort', sortCode)
  else url.searchParams.delete('sort')
  window.history.replaceState({}, '', url)
})
Passing defaultSort on the controller tells the SDK which code to use when execute() is called with no sort. Pass sort: undefined to fall back to that default.

Search sort dropdown

The pattern is identical — filter the metaobjects by the search scope and drive a client.search() controller instead.
{% assign layers_sorts = shop.metaobjects['app--278936322049--sort_order'] | sort: 'order' %}

<select id="search-sort" name="sort">
  <option value="">Relevance</option>
  {% for sort in layers_sorts %}
    {% assign scopes = sort.scope.value %}
    {% if scopes contains 'search' %}
      <option value="{{ sort.code.value }}">{{ sort.name.value }}</option>
    {% endif %}
  {% endfor %}
</select>
const search = window.layers.search()
const query = new URLSearchParams(location.search).get('q') ?? ''

search.subscribe(({ data }) => data && renderGrid(data.products))

await search.execute({ query, sort: undefined }) // relevance

document.querySelector('#search-sort').addEventListener('change', (event) => {
  search.execute({ query, sort: event.target.value || undefined })
})
Leave the first option blank (or label it “Relevance”) on search pages. Omitting sort lets Layers rank by relevance — usually what shoppers want for a typed query.

Combining sort with filters and pagination

Controller options merge across execute() calls. Passing only the field you want to change keeps the rest sticky.
// Page 2 of the current sort
collection.execute({ page: 2 })

// New sort, same filters and page reset to 1
collection.execute({ sort: 'price-asc', page: 1 })

// Same sort, additional filter
collection.execute({ filters: { color: 'Red' } })
If you’ve configured filter aliases, URL-friendly keys (color) resolve to API codes (options.color) without extra wiring.

Default sort orders

SurfaceDefault source
CollectiondefaultSort on the controller (or the controller’s first call) → falls back to the collection’s layers.default_sort_order metafield → falls back to the store default
SearchRelevance
Pass the metafield-resolved code (read in Liquid) as defaultSort so the SDK and the rendered dropdown agree on the default.

Why the SDK over Fetch

ConcernFetch versionSDK version
Driving the requestHand-rolled fetch to /browse or /searchcontroller.execute({ sort })
Re-rendersDIY DOM updatesSignal subscriber
Sticky optionsTrack manuallyMerged automatically across calls
Caching across navDIYRestored from localStorage

Troubleshooting

Selecting a sort doesn’t change the grid. Confirm the subscriber renders on every data change, not just the first one. The signal emits a new state on every execute(). The wrong default is pre-selected. Read request.params.sort and the layers.default_sort_order metafield in Liquid, mark the matching <option> as selected server-side, and pass the same code as defaultSort to the controller. Sort options are missing from the dropdown. Check Enable as Storefront Sort is on in the Layers dashboard, the metaobject’s scope array contains the right surface, and the same sort is listed in the SDK client’s sorts config.

See also