Guides / Building Search UI / UI & UX patterns

Facet dropdown with React InstantSearch

This is the React InstantSearch v7 documentation. React InstantSearch v7 is the latest version of React InstantSearch and the stable version of React InstantSearch Hooks.

If you were using React InstantSearch v6, you can upgrade to v7.

If you were using React InstantSearch Hooks, you can still use the React InstantSearch v7 documentation, but you should check the upgrade guide for necessary changes.

If you want to keep using React InstantSearch v6, you can find the archived documentation.

All Search and Discovery user interfaces display, in addition to a search box, a series of filters that allow users to narrow down their search. 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 increased its sales by +150% sales from Algolia’s search.

Lacoste search interface with filters in drop-down menus

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 drop-down menu layout.

Widget elements

By design, each facet drop-down menu widget has two elements:

  • A button with a label and the number of active refinements.
  • A panel (that can be toggled) containing the widget.

Both can be customized.

The facet drop-down menu widget can only contain one widget and is compatible with <DynamicWidgets>

Facet drop-down menu

Quick start

1. Clone/fork the code sample

To get started, you can clone the code sample (and follow the instructions to install the dependencies) or fork the following CodeSandbox.

In the project, you will see two components:

  • Panel.tsx used as the drop-down menu that can be toggled. It also displays a header, wraps your provided widget and adds a footer on mobile.
  • FacetDropdown.tsx used to get the number of active refinements for the provided widget and close the panel when the search state has changed.

To use the facet drop-down menu widget in your own project: install or duplicate the dependencies for each widget (like the hooks folder and utils.ts file).

2. Use the facet drop-down menu widget

A basic usage of the facet drop-down menu widget:

1
2
3
<FacetDropdown>
  <RefinementList attribute="type" />
</FacetDropdown>

The <FacetDropdown> widget automatically retrieves the attribute of the first provided child widget to display the number of current refinements in the drop-down menu toggle button.

Facet drop-down menu showing the number of current refinements

Props

buttonText (String | Function - Optional)

You can use the buttonText prop to customize the text displayed in the drop-down menu button. It can be a string or a function. By default, it shows the number of active refinement for the provided widget.

Here’s an example showing how you can get the current refinements to display the current bounds of a <RangeInput> widget.

1
2
3
4
5
6
7
8
9
10
11
12
<FacetDropdown
  buttonText={({ refinements }) => {
    const [start, end] = refinements;
    return start || end
      ? `Price (${(start || end).label}${
          end ? ' & ' + end.label : ''
        })`
      : `Price`;
  }}
>
  <RangeInput attribute="price" />
</FacetDropdown>

Facet dropdown with a custom button text

closeOnChange (Boolean | Function - Optional)

You can use the closeOnChange prop to close the drop-down menu as soon as users selects a facet value using a boolean or function that returns true. If it’s false, the drop-down menu isn’t closed automatically, allowing users to select more than one facet value.

Here’s an example where the drop-down menu is automatically closed only if users are using a device with a width greater than or equal to 375px.

1
2
3
4
5
6
7
8
9
const MOBILE_WIDTH = 375;
/* ... */
<FacetDropdown closeOnChange={() => window.innerWidth >= MOBILE_WIDTH}>
  <RefinementList
    attribute="brand"
    searchable={true}
    searchablePlaceholder="Search..."
  />
</FacetDropdown>

cssClasses (Object - Optional)

You can use the classNames prop to add CSS classes on multiple elements of the facet drop-down menu widget.

Here’s an example that adds custom CSS classes.

1
2
3
4
5
6
7
8
9
10
<FacetDropdown
  classNames={{
    button: 'MyButton',
    buttonRefined: 'MyButton--refined',
    closeButton: 'MyCloseButton',
    mobileTitle: 'MyMobileTitle',
  }}
>
  <RefinementList attribute="type" />
</FacetDropdown>

Customizing the UI

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

An example of a search screen with facet dropdowns

You can customize two aspects you can customize to your needs:

  1. Inline facet values

    The demo’s brand drop-down menu widget uses an inline list. (Code is in the App.css file.)

An example of a drop-down meu with inline facet values

Here’s the CSS:

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

    The demo’s category drop-down menu widget is hierarchical and is a fixed height list. (Code is in the App.css file.)

    An example of facets with a fixed height drop-down menu

    Here’s the CSS:

    1
    2
    3
    4
    
    .my-CategoryDropdown .ais-HierarchicalMenu {
      height: 185px;
      overflow: auto;
    }
    

Providing mobile support

Mobile devices are already supported by the widget. Below 375px (this breaking point can be changed, see below):

  • The drop-down menu position is fixed and taking the full page size.
  • The page scroll is locked when the drop-down menu is open.
  • The button text is added as a title at the top of the drop-down menu.
  • A button to apply the changes is added at the bottom of the drop-down menu.

If you want to customize the default behavior, the facet drop-down menu widget uses the following CSS media query:

1
2
3
@media only screen and (max-width: 375px) {
  /* ... */
}

It also uses the following logic to prevent page scrolling when the drop-down menu is open:

1
2
const isMobile = useMediaQuery('(max-width: 375px)');
useLockedBody(isOpened && isMobile);

This code sample 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?