Guides / Solutions / Ecommerce / Filtering and Navigation

All Search & Discovery UIs display, in addition to a search box, a series of filters that allow end users to narrow down their search. As explained in the docs, these onscreen filters are called facets. A typical facet is an attribute like “brand” or “price”, and facet values are the individual brands and prices. By clicking on a facet value, users can include and exclude whole categories of products. For example, by selecting “Blue” in the “Color” facet filter, a user can exclude every product except blue ones.

For a long time, ecommerce websites have been displaying facets on the left side of the screen, allowing easy access. But this placement reduces the space available to display products. With the rise of mobile-first design and touchscreen, online businesses have started to display facets at the top of their product listing. That’s the case of Lacoste which has increased its sales by +150% sales from Algolia’s search.

Gif image of Lacoste dropdown facets

Dropdown faceting offers two benefits:

  • It increases facet visibility and accessibility, encouraging more usage.
  • It simplifies the screen, leaving more room for products and creating more on-screen breathing space, an important UX design choice.

This guide shows how to turn a facet filter, commonly represented by a refinementList or a hierarchicalMenu widget, into a custom widget with a dropdown layout.

This guide shows how to turn a facet filter, commonly represented by a refinementList or a hierarchicalMenu, widget, into a custom widget with a dropdown layout. It addresses all businesses - those who haven’t yet implemented facets or those who have but would like to reposition them horizontally over their products.

The provided code uses InstantSearch.js, which can be taken as-is or as a reference if you want to replicate the same pattern using React, Vue, or Angular InstantSearch.

Preview

Screenshot of the demo showing a facet dropdown refinement list

Requirements

Difficulty
Intermediate
Prerequisites Instant Search v4.9.0

Widgets:

In addition, it uses thepanel widget, which acts as a wrapper for all the refinement widgets.

By design, each dropdown refinement widget wrapper has two elements:

  • A button with a label and facet count
  • A dropdown box

Both can be customized using the Dropdown settings. Each of the dropdowns come wrapped in its own <div />, which allows multiple facets in the same container, thus creating a horizontal list.

Screenshot of an automatically selected facet

To ease the creation of a dropdown refinement widget, this guide provides a factory wrapper function called createDropdown() that takes two parameters

  1. the refinement widget you want to turn into a dropdown
  2. an object with optional settings

This function, located in the file src/Dropdown.js, returns an InstantSearch widget that can be placed alongside other widgets on your search screen.

1
2
3
4
const myFacetDropdown = createDropdown(
  instantsearch.widgets.refinementList, 
  { dropdownOptionalSettings }
});

Quick start

First, you create your own dropdown widget. Later, you customize it. To create the dropdown, you use a refinementList widget on your type facet.

1 - Prepare the HTML markup

  • Import all JavaScript files, such as Algolia search, InstantSearch.js, and your own JS files.
  • Declare a <div id="type"> placeholder for the dropdown widget for the type facet.

Do this in index.html:

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container">
    <div id="searchbox"></div>

    <!-- Container where will be added all Dropdown facet filters -->
    <div class="search-panel__filters">
        ....
        <div id="type"></div>
        ....
    </div>

    <div id="hits"></div>
</div>

2 - Import Dropdown.js and create the widget dropdown

Import the createDropdown() function from the src/dropdown.js file before initializing an InstantSearch instance with your Algolia credentials.

Do this in src/app.js.

1
2
// Import of the Dropdown.js file
import { createDropdown } from './Dropdown';

Don’t hesitate to visit the Getting Started with InstantSearch.js guide if you want a quick refresher.

Once that’s done, create the Dropdown widget. In this case, use a refinementList widget for the type facet. As a result, you create a customized refinementListDropdown widget for the type facet that can also be re-used for other facets.

Do this in src/app.js.

1
2
3
4
5
// Creation of the refinementListDropdown widget
const refinementListDropdown = 
createDropdown(instantsearch.widgets.refinementList, {
  closeOnChange: true
});

Here, instantsearch.widgets.refinementList refers to InstantSearch’s refinementList widget. See the widget reference guide.

3 - Run your created widget in InstantSearch instance

Now that you’ve created your refinementListDropdown widget, you’re ready to add it to your InstantSearch instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Initialisation of InstantSearch and our widgets
search.addWidgets([
  instantsearch.widgets.searchBox({
    container: '#searchbox',
  }),
  
  instantsearch.widgets.hits({...}),
  
  // Adding the refinementListDropdown widget on the `type` facet
  refinementListDropdown({
    container: '#type',  // id of the <div> wh
    attribute: 'type',   // name of the face attribute in the dataset
    searchable: true
  }),
  ...
]);

search.start();

By design, CreateDropdown() creates a widget that acts as a wrapper around the initial widget passed in as a parameter. For that reason, the created refinementListDropdown can take any of the settings available in the refinementList widget. For example, { limit: 5, showMore: true } limits the default number of displayed facet values to “5”.

An example of a search screen with facet dropdowns

Settings available in the createDropdown()

As mentioned, the createDropdown() function takes 2 parameters:

  1. the refinement widget to turn into a dropdown [mandatory]
  2. an object with optional settings [optional]
1
2
3
4
5
6
7
8
9
createDropdown(
 baseWidget,
 {
   cssClasses: userCssClasses = {},
   buttonText,
   buttonClassName,
   closeOnChange,
 } = {}
) {

Here are several optional settings passed to the function using the second parameter, a typical JavaScript object.

cssClasses (Object - Optional)

This option expects an object that allows you to override the CSS class in your code. Here’s an example with default values:

1
2
3
4
5
6
cssClasses = {
  root: 'ais-Dropdown',
  button: 'ais-Dropdown-button'
  buttonRefined: 'ais-Dropdown-button--refined'
  closeButton: 'ais-Dropdown-close'
}

buttonText (String | Function - Optional)

This is the text displayed in the DropDown button. It can be a string or a function. By default, it shows the price of the active price refinement. Here’s an example from the priceMenuDropdown widget.

1
2
3
4
5
6
buttonText({ items }) {
   const refinedItem = (items || []).find(
     (item) => item.label !== 'All' && item.isRefined
   );
   return refinedItem ? `Price (${refinedItem.label})` : 'Price Menu';
 },

buttonClassName (String - Optional)

Same as buttonText, but for the ClassName. Here’s an example from the priceMenuDropdown widget.

1
2
3
4
5
6
buttonClassName({ items }) {
   const isRefined = (items || []).find(
     (item) => item.label !== 'All' && item.isRefined
   );
   return isRefined && 'ais-Dropdown-button--refined';
 },

closeOnChange (Boolean | Function - Optional)

This argument can be a boolean or a function that returns true if you want the dropdown to close as soon as a user selects a value. If it’s false, it doesn’t close automatically, thus enabling the end user to select more than one facet value. Here’s an example in which the code returns true if the end user is using a mobile device.

1
closeOnChange: () => window.innerWidth >= MOBILE_WIDTH

Customizing the UI

The generated markup allows you to customize the look and feel to your needs, to change the default aspect of the dropdown with a few lines of CSS. Here’s the default version:

An example of a search screen with facet dropdowns

There are two aspects:

1./ Inline facet values

The brandDropdown widget uses an inline list. Code is available in the app.css file.

An example of a dropdown with inlined facet values

Here’s the CSS:

1
2
3
4
5
.my-BrandDropdown .ais-RefinementList-list {
 width: 20rem;
 display: flex;
 flex-wrap: wrap;
} 

2./ Fixed height dropdown

The categoriesDropdown widget, which is hierarchical, uses a fixed height list. Code is available in the app.css file.

An example of facets with a fixed height dropdown

Here’s the CSS:

1
2
3
4
#category .ais-HierarchicalMenu {
 height: 195px;
 overflow: auto;
}

Providing mobile support

On mobile devices, more than anywhere else, displaying facet refinements over the results is a must-have, considering the limited real estate. Dropdown widgets are a great fit, but you need to tweak the display so they can take advantage of the screen’s width and height.

An example of dropdown facets for mobile

The mobile version uses the following CSS:

1
@media only screen and (max-width: 375px) { ... } // see the full code

It also uses the following JavaScript configuration at each dropdown widget level:

1
2
3
4
5
6
7
8
9
const MOBILE_WIDTH = 375;

const refinementListDropdown = createDropdown(
  instantsearch.widgets.refinementList,
  {
    closeOnChange: false,
    closeOnChange: () => window.innerWidth >= MOBILE_WIDTH,
  }
);

This demo gets you started by providing a basic look and feel for mobile devices. You might want to refactor the provided code to match your current design and thus offer the best experience to your mobile users.

Did you find this page helpful?