Skip to main content

Overview

This is the SDK equivalent of Implementing search in Liquid. The same four moving pieces apply: autocomplete, prepare, get, and facets and sort orders. Each is exposed as an SDK controller. The SDK handles request deduplication, prepared search IDs, abort signals, and reactive state for you. Use this version when you have already installed @commerce-blocks/sdk in your theme. For a no-dependencies version, see the Liquid + Fetch guide.

Prerequisites

  • The Layers Shopify app installed and synced.
  • The SDK installed and initialized with storage when you need to reuse prepared search IDs across pages. See the installation guide.
  • The Storefront Pixel installed. The pixel owns device and session identifiers on the storefront; pass them to createSDK() through identity as a static object or resolver for SDK-issued requests.

Controllers used

All expose the same reactive state and subscribe() shape as the collection controller.

1. Autocomplete in the header

autocomplete() fetches suggestions and supports semantic redirects. When a typed query matches a semantic redirect, data._meta.redirect.url is populated and you should navigate the shopper directly.
The controller handles abort and deduplication internally. No manual AbortController plumbing is required.

2. Prepare the search before the results page

prepare() is a first-class method on sdk.search(). Call it the moment a shopper commits to a search. The SDK kicks off the request, caches the resulting search_id against the query, and get() automatically reuses it on the next page. Option A - Prepare on submit. Best when you can intercept the form submit so prepare keeps running during navigation.
Option B - Prepare on the search results page. Simpler, but loses the in-flight head start.
Awaiting prepare() before navigation is what persists the prepared search_id for cross-page reuse. If you cannot await it, the results page still works, but it repeats the prepare step.
The SDK resolves the prepared search_id itself — get() does not read a search_id from the URL, so a shopper landing on a shared link without a matching prepare entry gets standard processing. Still put it there (/search?q=...&search_id=...) for pixel attribution on shared or bookmarked links — note the prepared cache is not reused on a fresh visit, so the ID in the URL preserves attribution, not speed. Also add original_query when an autocomplete suggestion click — rather than typed input — drove the search, so analytics can attribute the executed suggestion to what the shopper typed. The storefront pixel treats both as first-class URL parameters.

3. Get the search results

On the search results page, get() resolves the prepared search_id automatically when the query and request parameters match. You do not need to read an ID from the URL or pass one to get(). The SDK falls back to standard processing if the prepare entry has expired or no prepare ran.
Render the results page frame — title, search bar, sort/facet controls, and a skeleton grid — in Liquid so the first paint is complete, then hydrate in place. Injecting the frame from JS causes layout shift and hides the search bar while scripts load. For result cards, render tiles through the theme’s own card snippet via the Section Rendering API rather than a client-side card renderer.
A simple readActiveFiltersFromUrl helper builds a filter group from URL parameters. Replace the attribute names with the codes configured in your Layers dashboard:
Controller options merge across calls, so subsequent interactions only pass what is changing:

4. Render facet and sort controls at runtime

In v3, sort orders and facets are not configured at SDK init. Fetch them at runtime with sdk.sortOrders() and sdk.facets().
You can also request filtered facet counts by passing the active filterGroup:
The end-to-end timeline:
1

Header submit

Shopper presses enter. Create a search controller and await search.prepare() before navigating to /search?q=.... Cross-page reuse requires storage in createSDK(). The results page resolves the prepared search_id itself.
2

Results page render

Render the sort dropdown and empty facet groups from sdk.sortOrders() and sdk.facets(), including the active sort/filter state from the URL.
3

Get results

Page JS calls search.get({ sort, filterGroup, pagination }) — query is fixed when the controller is created. The SDK resolves the prepared search_id automatically. The subscriber renders the grid, facets, and pagination as soon as data lands.
4

Interaction

On sort change or filter toggle, call search.get({ ... }) with just the changed fields. Mirror the state to the URL via history.replaceState.

Why the SDK over Fetch

Troubleshooting

Suggestions show but redirects do not fire. The redirect lives at data._meta.redirect.url on the autocomplete state. Make sure your subscriber checks for it before rendering the suggestion list. get() is slow on the results page. Prepare was not called, or the cache has expired. The SDK falls back to normal processing. Confirm prepare runs before navigation. Filters do not apply. Attribute codes in filterGroup must exactly match the codes configured as facets in Layers. Fetch sdk.facets() to verify the codes.

See also