Shopware AJAX Product Filtering: Build a Seamless Filter Without Page Reloads
Shopware’s built‑in AJAX filter eliminates full‑page reloads during product filtering. This guide walks through enabling the component, customizing the UI, wiring JavaScript, and setting up cache strategies—complete with a worked example and trade‑off analysis.
06 Sept 2025, 19:34 UTC

Problem: Full‑Page Reloads Hurt Conversion
When a customer selects a filter on a product listing, the default Shopware behaviour is to submit a request and reload the entire page. This not only wastes bandwidth but also breaks the user’s mental flow and can increase bounce rates. The goal is to replace that synchronous request with an asynchronous one that updates only the product grid.
Thesis: Shopware’s Built‑In AJAX Filter Does Exactly That
Starting with Shopware 6.3 the storefront bundle ships a ready‑to‑use AjaxFilter component. It intercepts filter changes, sends an XHR request to /storefront/filter, and renders the new product list via Twig fragments. The component is fully supported in all later releases and can be extended with custom filter types.
1️⃣ Enabling the Component
- Activate the plugin in the administration UI:
Settings > System > Plugins. Find the AjaxFilter plugin and click Install then Activate. The plugin registers a service that hooks into the storefront request lifecycle. - Configure the filter route in
config/packages/shopware.yaml:
This tells the storefront to listen for filter requests on that endpoint. No code changes are required for the default behaviour.shopware: storefront: filter: enabled: true route: /storefront/filter - Set permissions for the route. The route is public, but you may want to restrict it to authenticated users. Add a guard in
src/Resources/config/routing.ymlif needed.
2️⃣ Customizing the Filter UI
The filter UI is rendered by a Twig block product_listing.filter. To add a custom filter type, override the block in a theme:
{% sw_extends '@Storefront/storefront/page/product-listing/index.html.twig' %}
{% block product_listing.filter %}
{{ parent() }}
{# Custom price range filter #}
Price
{% endblock %}
The data-filter-type attribute tells the JavaScript component which filter logic to apply. The component ships with handlers for category, price, and attribute. If you need a new type, register a handler in a custom module.
3️⃣ Wiring JavaScript and Routes
Shopware bundles the storefront/js/storefront/filter.js script. Ensure it is loaded by adding the following to your theme’s main.js:
import 'src/storefront/filter.js';
The script listens for change events on the filter form, serializes the data, and POSTs it to the /storefront/filter endpoint. The server responds with a JSON payload containing the rendered HTML fragment for the product grid.
Verify the request in the browser dev tools:
- Open the product listing page.
- Select a filter.
- Check the Network tab: you should see a POST to
/storefront/filterand a200 OKresponse containing<div id="product-grid">.
4️⃣ Caching and Performance
Because the filter endpoint returns HTML fragments, caching is essential to keep response times low. Shopware’s default setup uses Symfony’s fragment caching with Redis as the backend. To enable it:
- Configure Redis in
config/packages/cache.yaml:framework: cache: pools: cache.default_storefront: adapter: cache.redis.default - Set cache tags on the filter response. The
AjaxFilterplugin automatically tags fragments withproduct_listingandfilter_{hash}. When product data changes, these tags are invalidated. - Test cache invalidation by updating a product price and ensuring the filter result reflects the change within a few seconds. Use Redis CLI
redis-cli keys *to inspect cache keys if needed.
Worked Example: Adding a Custom Price Range Filter
Below is a minimal, reproducible setup:
- Create a new plugin
CustomFilter:bin/console plugin:create CustomFilter - Add a Twig override in
src/Resources/views/storefront/page/product-listing/index.html.twig(see section 2). - Register a JavaScript module to handle the new filter type in
src/Resources/app/storefront/src/custom-filter.js:import { Filter } from 'src/storefront/filter.js'; class PriceFilter extends Filter { constructor() { super(); } apply() { const min = this.element.querySelector('[name="price_min"]').value; const max = this.element.querySelector('[name="price_max"]').value; return { price_min: min, price_max: max }; } } window.customFilters = window.customFilters || {}; window.customFilters.price = new PriceFilter(); - Import the module in your theme’s
main.js. - Clear caches:
bin/console cache:clear --env=prod. - Reload the storefront. The price filter should now appear, and selecting a range triggers an AJAX request.
Check the console for any JavaScript errors. If the filter does not appear, verify that the Twig block is correctly overridden and that the data-filter-type matches the handler name.
Trade‑Offs and Limitations
- JavaScript dependency: If a visitor disables JS, the component falls back to a full page reload. Ensure graceful degradation by keeping the default form action intact.
- Mobile rendering: The filter UI may stack vertically on narrow screens. Test on various devices and adjust CSS accordingly.
- Cache complexity: Misconfigured cache tags can lead to stale results. Always verify that cache invalidation rules are triggered after product updates.
- SEO considerations: The AJAX endpoint returns only the product grid. Search engines will still index the full page, but the dynamic content will not be crawled. Use canonical URLs and meta tags as usual.
Actionable Checklist
- Enable
AjaxFilterplugin and route inshopware.yaml. - Override the
product_listing.filterblock for custom filters. - Import
storefront/filter.jsin your theme’smain.js. - Configure Redis fragment caching and verify cache tags.
- Test the filter on desktop and mobile; confirm XHR requests.
- Monitor cache hit ratios in Redis after traffic spikes.
- Document fallback behaviour for no‑JS scenarios.
By following this guide, you’ll deliver a smoother, faster filtering experience that can boost conversion rates and reduce server load.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.