Search & Filtering

Shopify provides two related but distinct systems for helping shoppers find products: full-page search, and filtering within a collection (narrowing by price, size, color, and similar facets). Both are built into the platform rather than requiring a separate search app for basic use.

The search template and object

A search results page uses templates/search.json, and the search object exposes the query and its matches:

Liquid a basic search results page
<p>{{ search.results_count }} results for "{{ search.terms }}"</p>
{% for item in search.results %}
  {% if item.object_type == 'product' %}
    {% render 'product-card', product: item %}
  {% endif %}
{% endfor %}

search.results can mix products, articles, and pages in one list — checking object_type is how a template tells them apart before deciding how to render each one.

Predictive search

Predictive search is the dropdown of live suggestions that appears while a shopper is still typing, well before they submit the search form. It works by calling a dedicated endpoint from JavaScript as the input changes, rather than waiting for a full page load:

JavaScript calling the predictive search endpoint
const res = await fetch(`/search/suggest.json?q=${encodeURIComponent(query)}&resources[type]=product`);
const data = await res.json();
renderSuggestions(data.resources.results.products);

Filtering a collection

On a collection page, collection.filters describes every facet available for that collection — price range, product type, size, color, and any filterable metafields or variant options a merchant has enabled — along with which values are currently active:

Liquid rendering filter checkboxes
{% for filter in collection.filters %}
  <fieldset>
    <legend>{{ filter.label }}</legend>
    {% for value in filter.values %}
      <label>
        <input type="checkbox" name="{{ value.param_name }}" value="{{ value.value }}"
          {% if value.active %}checked{% endif %}>
        {{ value.label }} ({{ value.count }})
      </label>
    {% endfor %}
  </fieldset>
{% endfor %}

Submitting these as query-string parameters re-requests the collection page with the filters applied server-side — Shopify handles the actual matching logic, the theme just needs to render the available options and reflect which ones are active.

The section-rendering API

Reloading the whole page every time a shopper toggles a filter feels sluggish. The section-rendering API lets a theme re-request just one section's HTML for a given URL and swap it into the page with JavaScript, keeping the rest of the page (header, footer) untouched:

JavaScript re-rendering just the product grid section
const url = `${window.location.pathname}?section_id=product-grid&${params}`;
const html = await (await fetch(url)).text();
document.querySelector('#product-grid').innerHTML = html;
Note: search and filtering both respect a store's existing inventory and availability rules automatically — a sold-out or hidden product won't surface in search results or filtered listings without any extra logic in the theme.