Skip to content

Deep Dive · Product Discovery

Custom Filtering UI on Shopify Search & Discovery

How a custom mobile filter bar, price range control, and view toggle remained connected to Shopify's native facet engine instead of replacing it.

  • Search & Discovery
  • Facets

A collection page is where a shopper narrows a catalog down to the few products they might actually buy. The interface for that can be entirely custom. The engine underneath it does not have to be, and on Absolute Support it was not. This is how a custom mobile filtering experience stayed connected to Shopify's native facet engine instead of replacing it.

This is the second of three deep dives from the Absolute Support case. It follows the compression PDP deep dive and leads into the merchant-editable sections deep dive.

The problem, in one paragraph

Shoppers arrive at a compression catalog from different starting points. Some think in garment type (socks, thigh highs, pantyhose, sleeves). Some think in compression level. Some think in a condition or use category. Some think in gender or a general catalog category, and some just want to sort by price. The collection page had to present a custom, brand-specific filtering experience for all of those, on a phone first, without turning filtering itself into custom code that could drift out of step with Shopify.

Constraints

  • Shopify already owns filtering. Shopify's Search & Discovery computes and applies facets, and it keeps the filter state in the collection URL. A "facet," for readers new to the term, is one filter dimension such as size, color, or price.
  • Collection URLs must stay meaningful. A filtered collection is a shareable, indexable URL. A custom engine that invented its own state would break that.
  • Sorting and results are platform behavior. Sort order and the rendered product grid come from Shopify. The custom layer should present them, not recompute them.
  • One filtering engine, not two. The risk with any custom filter UI is quietly building a second source of truth for "what is filtered." That was the thing to avoid.

A note on ownership before the implementation: the catalog taxonomy here is part Shopify configuration and part custom presentation. The facets a collection exposes are configured in Shopify's admin. The theme renders and restyles them. I configured and presented; I did not build the facet engine.

The decision: custom shell, native engine

The interface is authored. The filtering state and the results stay Shopify-native.

Custom mobile filter UI      (authored presentation)
  filter bar · price slider · view toggle
        |
        v
Native facet form            (Shopify)
        |
        v
Search & Discovery           (Shopify engine)
        |
        v
Updated collection results   (Shopify-rendered)
  product URLs · sorting · filter state

On mobile this is a vertical hand-off. On desktop the same relationship can lay out horizontally. Either way, the custom controls sit on top of the native facet form and pass their actions into it.

How the pieces connect

The custom controls do not run their own filtering. They update the inputs of Shopify's facet form, and let Shopify do the work.

The custom controls write to the native form. When a shopper changes a filter in the custom UI, that action updates the corresponding native facet input. Shopify's facet mechanism then requests the filtered results and re-renders the grid. The results, the counts, and the URL are Shopify's output.

Re-rendering is handled after each update. Because the results region is replaced when filters change, the custom controls are re-initialized after each update so they stay attached to the freshly rendered facet markup rather than to a stale copy.

The facets themselves are admin-configured. On the live storefront the collection facets include gender, a height or length type, a foot detail such as closed or open toe, size, color, price, and availability. Compression level is expressed through the collection and navigation structure rather than as an in-collection facet, which is a deliberate taxonomy choice worth noting.

The custom interface pieces

The authored layer is a small set of parts:

  • A mobile filter bar that opens the filter controls.
  • A filter panel that holds the facet options.
  • A dual-handle price range slider layered over the native price inputs.
  • A grid or list view toggle for the product cards.
  • A remembered view preference, stored on the client so the shopper's grid-or-list choice persists between visits.
  • A sort presentation that surfaces Shopify's native sort options in the custom UI.

The price slider is the one piece that needs the most care, because it is a custom control standing in front of native minimum and maximum inputs. Its job is to move those inputs, not to become the authority on price. The view toggle is deliberately the opposite kind of state: it is a presentation preference, and it must never behave like a second definition of the filtered catalog.

The tag-based product finder

Separate from the collection filters, the storefront includes a product finder. It is worth being precise about what it is.

A shopper selects a gender or catalog category, a compression level, and a style. The finder then builds or routes to a matching collection URL using the store's tag structure. That is the whole mechanism.

It is a navigation shortcut into the catalog. It is not a quiz. It is not AI. It is not a recommendation engine, and it is not medical advice. Calling it any of those would misrepresent a tag-to-collection router as something it is not.

Mobile-first filtering UX

Most of the design attention went here, because filtering on a phone is where a good catalog either helps or frustrates.

Some behaviors are implemented and verifiable in the build:

  • The filter controls open from a mobile filter bar, so filtering is reachable within thumb range rather than buried.
  • The price slider gives a direct way to set a range on a small screen instead of typing into two fields.
  • The grid or list preference is remembered across visits.
  • A drawer visibility fix keeps the full facet list open on mobile, correcting a default that could collapse it.

Other behaviors I treat as design goals and open verification items rather than claims, because they were not formally tested:

  • Touch targets meeting a comfortable minimum for the filter bar, slider handles, and toggle.
  • Preserving scroll position through a filter change where the platform supports it.
  • Avoiding nested scroll traps inside the filter panel.
  • A clear, obvious return from the filter panel back to results.
  • Predictable browser-back behavior, given that filter state lives in the URL.
  • A readable count of active filters, and visible apply or reset actions where present.

I would rather list those as things to confirm than imply an accessibility result I did not measure.

What stayed native

The custom layer never took ownership of these:

  • The Search & Discovery facet engine
  • Filtering state, held in the collection URL
  • Sorting
  • Collection rendering
  • Product URLs
  • Search

Verification

Implementation-level, not a business metric:

  • The custom controls pass their actions into the native facet form.
  • Filtering and sorting remain Shopify-native.
  • The mobile presentation is custom.
  • The grid or list preference is remembered on the client.
  • The finder routes through tag-based collection logic, not through any scoring or recommendation.

Trade-offs

  • Tag-based discovery depends on clean data. Because the finder routes on tags, inconsistent tagging can send a query to an empty result. This is data hygiene, not a defect in the interface.
  • The dual-handle slider needs careful synchronization. A custom control in front of native inputs has to keep them exactly in step, or the visible range and the applied range can disagree.
  • Search & Discovery changes may need reconciliation. The custom layer sits on top of Shopify's facet markup, so a platform change there needs a hands-on pass.
  • A remembered view preference is client state. It is fine as a presentation preference, but it must stay that. It should never grow into a second definition of the catalog.

What I would improve next

  • Normalize the product tags so finder routes never dead-end on a naming mismatch.
  • Strengthen empty-result handling so a filtered view with no products guides the shopper somewhere useful.
  • Add filter-state integration tests that assert the URL and the rendered results agree after a change.
  • Verify keyboard operation and touch-target sizes across the filter bar, slider, and toggle.
  • Document the taxonomy and tag conventions the discovery paths rely on.

Next: A Merchant-Editable Section Layer on Dawn, which is how the rest of the storefront stays editable without turning the theme into a page builder.

Back to the caseAbsolute Support

Need a custom Shopify experience without replacing the platform underneath?

I design and build storefront layers around real catalog, merchandising, and platform constraints, while keeping purchase-critical systems reliable and maintainable.

Start a conversation