Filters, sorting and pagination
Search & discovery lists products live from Shopify, with the filters of Shopify's Search & Discovery app, sorting and pagination designed in Webflow. Covers the block, every filter type, sorting, the count and active filters, pagination with page numbers and links that keep the filters.
On this page
Search & discovery lists products straight from Shopify and lets the visitor filter, sort and page through them. The filters are the ones you set up in Shopify’s Search & Discovery app, such as colour, size, availability or price. You design each filter, the sort menu, the product card and the pagination in Webflow, and Smootify fills them.
Note
Before you start:
- install Shopify’s free Search & Discovery app and create your filters in it (Filters, then Add filter). Note the label of each filter: you will type it in Webflow;
- turn on the Search & discovery add-on in the Smootify dashboard. The Lite and Server plans include it. On your
.webflow.ioaddress it works without it, so you can build and test first.
How it fits together
What Smootify needs:
- a
smootify-search-discoveryelement; - inside it, one product card: a
smootify-productelement. Smootify repeats it once per product. Without a card, the block is removed.
What is up to you:
- the layout: where the filters, the sort menu, the count and the pagination sit, and how they look;
- which filters you show, and with which kind of element: checkboxes, a dropdown, swatches…;
- whether the filters sit inside the block or in a panel elsewhere on the page.
1. Add the block and the card
- Add a Custom element with the tag
smootify-search-discovery. - Inside it, add a Div block for the grid, and inside that a
smootify-productwithdata-id="filter". Design it as a product card.
The first smootify-product is the template: each product of the results gets a copy of it. So every element of the card needs a Smootify attribute, such as product="title" or variant="image". Text bound only to a CMS field would show the first product’s text on every card.
A Collection list inside. You can use a Collection list of product cards as the grid. Its first Collection item becomes the template, and the other items stay on the page until the first results replace them. Search engines then see real products, and the page keeps its height while it loads. The cards can keep data-id bound to the Shopify ID field, or use data-id="loader": Smootify sets them to filter when the page loads. See placeholder cards.
Which products. Without other settings, the block lists the whole catalog. Three attributes narrow it, and the visitor cannot remove them, not even with the reset:
data-collectionwith the collection’s Shopify ID: the products of one collection. Use it on the collection page.data-vendorwith the vendor’s name, as written in Shopify: the products of one vendor. Use it on the vendor page. It needs the Vendor filter turned on in Shopify’s Search & Discovery app, asdata-tagneeds the Tag filter.data-tagwith a product tag, such asdata-tag="summer": the products with that tag. Next todata-collectionit works differently: see Search inside a collection.
How many per page. limit sets the number of products per page: 50 by default, 250 at most. Each card is a full product, so a lower number, such as 24, keeps the page fast, especially on phones.
When a filter picks an option value, such as Colour: Red, each card shows its variant with that value, with its image and price.
2. Add the filters
Each filter element shows one filter of Shopify’s Search & Discovery app. Put it inside smootify-search-discovery and give it the custom attribute label with the filter’s label, written exactly as in the app. Pick the element by how the filter should look:
Checkboxes
- Built with
- A Form Block with one Checkbox.
- Label
label="Color"- Picks
- Several values.
Radio buttons
- Built with
- A Form Block with one Radio button.
- Label
label="Size"- Picks
- One value.
The browser's own select
- Built with
- A Form Block with a Select field.
- Label
label="Material"- Picks
- One value.
A Webflow Dropdown
- Built with
- A Form Block and a Webflow Dropdown.
- Label
label="Material"- Picks
- One value.
A panel over the page
- Built with
- A Form Block and a popover with links or buttons.
- Label
label="Material"- Picks
- One value.
Color or image buttons
- Built with
- A Form Block and one Custom element with the tag
button. - Label
label="Color"- Picks
- One value, or several with
multiple="true".
A price range
- Built with
- A Form Block with two number fields and two sliders.
- Label
- None: it reads no label.
- Picks
- A price range.
A search field
- Built with
- A Form Block with a Text field.
- Label
- None: it reads no label.
- Picks
- Words to search.
Without its Form Block, or without a label where it needs one, the element is removed.
Design one value only. The first Checkbox, Radio button, Dropdown link or button is the template, and Smootify copies it for every value of the filter. In a Select field, the first option stays as the placeholder, and Smootify writes the values after it.
Choices apply at once. A change reloads the products. For a panel that applies on a click, see the apply button in step 6.
Attributes on each filter element:
show-countadds the number of products next to each value, such as “Red (12)”; addhide-parenthesisfor “Red 12”. A value with no products gets the classempty-count.selected-value="Red"selects that value when the page opens. Onfilter-checkbox, list several with commas. The visitor cannot remove a preselected value unless you addallow-reset="true".custom-label="Colour"changes the name shown in the active filters (see step 4).
A filter whose values are all gone hides itself. On smootify-search-discovery, hide-empty also hides the values with no products, and hide-if-single hides a filter that has only one value.
What the app decides. Each filter’s settings in Shopify’s Search & Discovery app apply on the site too. Values you group in the app show as one value, and the values keep the order you set there: Smootify does not sort them again. The behaviour you choose for a filter, such as whether two values combine with AND or OR, is applied by Shopify to the results. Give each filter of the app one element: to show the filters in a mobile panel, move them there with data-expand (step 6) rather than making a second copy.
Languages. Shopify translates the filter values, with these rules of its own:
- the values of the Vendor and Tag filters are never translated: vendors always show in the store’s default language;
- the Tag filter shows only to visitors browsing in the store’s default language;
- translations follow the languages published in Shopify, not your markets.
Swatches
Inside the template button of filter-swatches, swatch="bg-color" paints the value’s colour as a background, swatch="color" as the text colour, swatch="image" shows its image, and swatch="title" its name. The colour or image is the value’s swatch in Shopify’s filter: see Swatch colours and images. The chosen button gets the class is-active. More in filter swatch parts.
filter-swatches keeps only its first button. Put any other button, such as an apply button, outside it.
Price
filter-price is always the price filter, so it needs no label. Inside it:
- a Form Block with two Text fields of Type Number, for the minimum and the maximum, and two range sliders. Webflow’s form fields have no range type: add the two sliders with a Code Embed inside the form;
- an element with
filter-price="progress": the bar between the two handles; - optionally,
filter-price="from-label"andfilter-price="to-label"on text, for the selected minimum and maximum.
It needs exactly two number fields and two sliders: see price filter parts. The range starts from the prices of the first results. Shopify shows the price filter only in the shop’s default currency.
Style the sliders in the Code Embed. Smootify adds no style to them: write their CSS in the same Code Embed, for example the size and colour of the handles. Smootify places filter-price="progress" with its left and right edges, as a percentage of the track, so give it an absolute position inside a parent with a relative position.
Collection
label="Collection" makes a filter of all your collections. It works with the single-choice elements (filter-list, filter-select, filter-dropdown, filter-popover, filter-swatches), not with filter-checkbox. While a collection is chosen, the text search looks inside it: see Search inside a collection.
Text search
filter-search holds a Form Block with a Text field. Typing searches after a short pause. If the form has a submit button, the search runs on submit instead. It reads no label.
Search inside a collection
The text search also works on a collection: with data-collection, or while the visitor has chosen a collection in a Collection filter. Shopify has no search inside a collection, so Smootify compares two lists and shows the products in both:
- the results of the search, up to 1,000, Shopify’s limit;
- the products of the collection, up to 2,000. In a bigger collection, only the first 2,000 are compared.
What changes compared with a plain search:
- Order. Relevance and price follow the order of the search. Best selling, title and date follow the order of the collection.
- Filters. Their values and counts come from the search, so they count the whole catalog, not only the collection.
- Pages. Previous and next, and the page numbers, work as usual.
For an exact search, use a tag. On a large or important collection, give its products a tag in Shopify, such as summer, and add data-tag="summer" to the block next to data-collection:
- without a search, the block still shows the collection, in its order;
- with a search, Smootify searches only the products with that tag. The results are exact, and the filters count only those products.
data-tag also works without data-collection: the block then always lists the products with that tag.
Warning
data-tag needs the Tag filter turned on in Shopify’s Search & Discovery app. Without it, Shopify ignores the tag without an error, and the tag narrows nothing. The debugger tells you when this happens.
3. Sorting
A sort element goes inside smootify-search-discovery, like a filter, with a Form Block inside it. There are four:
The browser's own select
- Built with
- A Form Block with a Select field.
- Good to know
- The first option stays as the placeholder.
Radio buttons
- Built with
- A Form Block with one Radio button.
- Good to know
- The Radio button is the template of every option.
A Webflow Dropdown
- Built with
- A Form Block and a Webflow Dropdown.
- Good to know
- The toggle shows the current sort.
A panel over the page
- Built with
- A Form Block and links or buttons in a popover.
- Good to know
- The trigger shows the current sort.
The options come from Shopify, and depend on the list:
- the whole catalog: relevance, and price low to high and high to low;
- one collection (
data-collection, or a chosen Collection filter): also best selling, title A to Z and Z to A, and oldest and newest first.
selected-value on the sort element picks an option when the page opens, by its label, such as selected-value="Price (Low to High)". Without it the store’s own order stays, and the trigger keeps the text you wrote in Webflow, such as “Featured”, until the visitor picks an option: Shopify does not tell which order a collection uses. Next to the element that shows the chosen order, a label of yours such as “Sort:” stays as you wrote it: the trigger then reads “Sort: Price (Low to High)”.
Sort labels
Each option has an English label, and each list shows only some of them:
| Key | Label | Shown for |
|---|---|---|
relevance | Relevance | every list |
price_asc | Price (Low to High) | every list |
price_desc | Price (High to Low) | every list |
best_selling | Best Selling | one collection |
title_asc | Title (A - Z) | one collection |
title_dsc | Title (Z - A) | one collection |
created_asc | Oldest to newest | one collection |
created_dsc | Newest to oldest | one collection |
To change them, go to Site settings › Custom code and add this script to Head code. Write only the labels you change: the others keep their English text.
<script>
window.SmootifySortLabels = {
relevance: "Featured",
price_asc: "Price, low to high",
price_desc: "Price, high to low",
best_selling: "Best sellers",
title_asc: "Name, A to Z",
title_dsc: "Name, Z to A",
created_asc: "Oldest first",
created_dsc: "Newest first",
};
</script>selected-value matches the label as shown: after a change, write the new label, such as selected-value="Price, low to high".
Several languages. With Webflow Localization, each locale sets the page language in <html lang>. Pick the labels from it, and keep the English ones for the other locales:
<script>
window.SmootifySortLabels = {
it: { relevance: "In evidenza", price_asc: "Prezzo crescente", price_desc: "Prezzo decrescente" },
fr: { relevance: "Pertinence", price_asc: "Prix croissant", price_desc: "Prix décroissant" },
}[document.documentElement.lang.slice(0, 2)] || {};
</script>4. Count, active filters and reset
These elements go anywhere inside smootify-search-discovery. All are optional.
filter="count"on text shows the total number of results.filter="active"is the template of one active filter: a Div block, a link or a Custom element with the tagbutton. Smootify makes one copy per active filter, and a click on a copy removes that filter. Inside it,filter="active-label"shows “Colour: Red”.filter="active-count"shows how many filters the visitor can remove.- A reset button: a Custom element with the tag
buttonandtype="reset". It removes every filter the visitor can remove, and the search words. It is disabled and getsis-disabledwhen there is nothing to remove. filter="empty-state"on a Div block shows only when no product matches.
Filters the visitor cannot remove, such as a value preselected without allow-reset, come first among the active filters, with the class preselected-filter, and a click on them does nothing.
5. Pagination
Pagination buttons are Custom elements with the tag button inside smootify-search-discovery. Their data-action decides how the visitor moves through the results:
Previous and next
- Shows
- One page at a time.
- Good to know
- The best choice for large catalogs.
Page numbers
- Shows
- One page at a time, with the numbers to jump to, between previous and next.
- Good to know
- On a collection, it needs the Availability filter (see Page numbers).
Load more
- Shows
- The next page, added below the products shown, on a click.
- Good to know
- Every page adds cards, which slows phones down on large catalogs.
Load as the visitor scrolls
- Shows
- The next page, added below when the button scrolls into view.
- Good to know
- Every page adds cards, which slows phones down on large catalogs.
A button is hidden and disabled when there is no such page. Use previous and next, or one of the two load buttons, not all of them. Buttons inside a product card, such as the previous and next of a booking calendar, are not pagination buttons.
When new results appear, the page scrolls to the block. disable-scroll on smootify-search-discovery turns this off, and disable-scroll-on-load-more turns it off for the load buttons only.
Page numbers
Next to previous and next, the block can show page numbers, such as 1 … 4 5 6 … 12:
- One number button. A Custom element with the tag
buttonanddata-action="page"is the template of every number: Smootify copies it once per number shown. Inside it,filter="page-number"holds the number; without it, the number becomes the button’s text. The current page gets the classis-activeand cannot be clicked: style it with a combo class. - The gap.
filter="page-ellipsis"is the template of the gaps between numbers, such as…. It is optional. When the gap is a single page, that page’s number shows instead. - How many numbers.
page-rangeonsmootify-search-discoverysets how many pages show on each side of the current one,1by default. The first and the last page always show. - “Page 3 of 12”.
filter="page-current"andfilter="page-total"on text. The total stays empty until Smootify knows it. - The class
has-page-numbersis onsmootify-search-discoverywhile there is more than one page to number: use it to hide the number bar otherwise. - In the address. With
sync-query-params, the address also gets the page from the second one on, such as?page=3, and a link with?page=3opens that page.
Note
On a collection, page numbers need the Availability filter turned on in Shopify’s Search & Discovery app. Shopify does not give the number of products in a collection, so Smootify adds it up from that filter. Without it the numbers do not show (previous and next still work), filter="count" shows only the products of the page, and on .webflow.io the debugger says why. A search counts at most 1,000 results, a Shopify limit.
Opening a page far from the current one takes a moment longer: Shopify gives the pages one after the other, so Smootify first reads the pages before it, with small requests. The pages already seen, and the next and previous ones, open at once.
6. Links, mobile panels and an apply button
Filters in the address. With sync-query-params on smootify-search-discovery, the address follows the filters, such as ?Color=Red, so a filtered page can be shared. Locked filters stay out of it. Even without it, a link to the page with a filter’s label and a value, such as /shop?Color=Red, opens it with that filter on, as long as the page has a filter element with that label. ?q= followed by words opens it with that search.
Filters outside the block. Filters and sort elements can sit in a panel elsewhere on the page, such as a side panel on mobile. Give the panel an ID in its element settings, and add data-expand="#filters" (with that ID) to smootify-search-discovery.
Filters in a popover. With auto-close-details="true" on smootify-search-discovery, a popover of filters inside it closes once the results change. See Popovers.
An apply button. A Custom element with the tag button and type="submit", inside the block but outside the filters’ forms, changes how filters apply: a choice updates the filters only, and the products reload when the visitor clicks the button.
Other settings
On smootify-search-discovery:
animation(fadeby default,slide,scale,fadeBlurred,slideBlurredornone),duration,easingandstaggerset how new cards appear;unavailable-productssets where products that cannot be bought go:LAST(the default),HIDEorSHOW. It does not apply inside a collection;avoid-webflow-restartskips restarting Webflow interactions after each render.
Every attribute is in smootify-search-discovery and search & discovery attributes.
While it loads. Add skeleton to the filters, the count and the card elements that should shimmer until the first results. Smootify removes it when it writes them. See loading.
Before you publish
Shopify’s own limits apply: a collection with more than 5,000 products, or a search with more than 100,000 results, shows no filters, and a filter shows at most 100 values. When a filter has more, group similar values in the app: a group holds at most 200 values, and a store can have at most 1,000 groups.
Check these first
- A filter never shows. Its
labelis not written exactly as in Shopify’s Search & Discovery app, or none of the listed products has a value for it. - A filter element disappears. It has no Form Block inside, or it sits outside
smootify-search-discoveryand outside thedata-expandpanel. - Every card shows the same name or image. Those elements are bound to CMS fields only. Add
product="title",variant="image"and the other attributes. - Only three sort options. Best selling, title and date exist only inside a collection.
data-tagchanges nothing. The Tag filter is off in Shopify’s Search & Discovery app. Turn it on.data-vendorchanges nothing. The Vendor filter is off in Shopify’s Search & Discovery app. Turn it on.- A search in a big collection misses products. Only the first 2,000 products of the collection are compared with the search. Give the collection’s products a tag and use
data-tag. - The count shows only the products of the page, and no page numbers appear. On a collection, the total comes from the Availability filter. Turn it on in Shopify’s Search & Discovery app.
- The page is slow on phones. Lower
limit, and usepreviousandnextinstead of loading more.
Coming from 1.x
- Nothing shimmers by default any more: add
skeletonwhere you want it. filter="count"is now the total number of results; 1.x showed the products of the page.filter-searchreads nolabel, andfilter-priceis always the price filter.filter-swatcheskeeps its first button and removes the others, a submit button included.- The text search works on collection pages: 1.x hid
filter-searchthere with thehide-searchclass, which 2.0 no longer sets. Remove a sharedfilter-searchfrom the collection pages if they should not have one.