Building Dynamic Filtering Systems without Apps
Historically, Shopify themes relied on clunky tag-based filtering. If you wanted advanced, multi-select, faceted filtering (like filtering by Size, Color, and Brand simultaneously), you had to pay $50/month for an app. These apps often ruined page speed by injecting massive JavaScript payloads to render the products.
Since the introduction of Shopify's native Storefront Filtering, you can build blazing-fast, app-free filters directly in your theme.
1. Rendering the Filters via Liquid
Shopify provides the collection.filters object. You can iterate through this object to render
checkboxes, price sliders, and color swatches natively.
<form id="CollectionFiltersForm">
{% for filter in collection.filters %}
<details class="filter-group">
<summary>{{ filter.label }}</summary>
<div class="filter-group-display">
{% for value in filter.values %}
<label>
<input type="checkbox"
name="{{ value.param_name }}"
value="{{ value.value }}"
{% if value.active %}checked{% endif %}
{% if value.count == 0 and value.active == false %}disabled{% endif %}
>
{{ value.label }} ({{ value.count }})
</label>
{% endfor %}
</div>
</details>
{% endfor %}
</form>
By default, submitting this form will trigger a full page reload, appending query parameters to the URL
(e.g., ?filter.v.option.color=Red).
2. Adding AJAX for Instant Updates
To make the experience seamless, we want to update the product grid without a full page reload. We can intercept the change events on the checkboxes, fetch the new HTML using the Section Rendering API, and inject it into the DOM.
document.getElementById('CollectionFiltersForm').addEventListener('change', function(e) {
const form = e.currentTarget;
const formData = new FormData(form);
const searchParams = new URLSearchParams(formData).toString();
// Update the browser URL without reloading
history.pushState(null, '', `?${searchParams}`);
// Fetch only the product grid section
fetch(`${window.location.pathname}?section_id=product-grid&${searchParams}`)
.then(response => response.text())
.then(html => {
// Parse the returned HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Extract the new grid and replace the old one
const newGrid = doc.getElementById('product-grid-container').innerHTML;
document.getElementById('product-grid-container').innerHTML = newGrid;
})
.catch(error => console.error('Error fetching filters:', error));
});
3. Benefits of this Approach
- SEO Friendly: Because the filters generate standard URLs, search engines can still crawl
them (if configured in
robots.txt). - Lightning Fast: You are relying on Shopify's highly optimized edge caching to return the HTML, rather than querying a third-party app's external server.
- Zero Cost: You completely eliminate the recurring monthly cost of an external filtering app.