Guides / Solutions / Ecommerce / Filtering and Navigation

Facets are a great way to let your users refine their search, but it can be overwhelming for them to find the right ones if they see a long list of facets. A solution to this problem is to guide your users’ discovery experience by showing them the most used category of facets at the top close to the search bar.

Screenshot of a guided search

In this image, and in the live demo, you see a set of guided search facets appear near the search bar. For example, if you are looking for computers, instead of typing out the query, you can click on Computer & Tablets to select the category facet filters. Not only do the results on the page get updated, the rest of the facet filters are also updated to stay contextual to the scope of the query. After you click on the Computer & Tablets facet, the brand facets get updated to show all the relevant brands such as HP and Apple. This guides your users to refine and improve the relevance of their results. The order of the facet values are based on count but can be easily adjusted to alphabetical order or any custom order that makes sense for your business.

Requirements

Difficulty
Beginner
Prerequisites Instant Search 4+

Implementation guide

In this guide, you build a classic ecommerce search page with a search box, guided search facets at the top, two facets (brand and price), and results. The guide will focus on how to build the guided search facets with InstantSearch JS.

High level approach

To create guided search facets, you need to create your own UI of the refinementList widget using connectors. Essentially, you override the out-of-the-box behavior of a refinement list widget with your own render function. You can find the code examples in the guide on the custom refinement list connector.

Before you get started, please make sure you:

File and index structure

For this implementation, you need two files:

  • index.html
  • main.js

You also need an Algolia index containing the ecommerce dataset. For this guide, name the index instant_search_solutions_ecommerce.

It’s a 3-step process (this code is located in main.js):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  // 1. Create a render function
  const renderRefinementList = (renderOptions, isFirstRender) => {
    // Rendering logic
  };

  // 2. Create the custom widget
  const customRefinementList = instantsearch.connectors.connectRefinementList(
    renderRefinementList
  );

  // 3. Instantiate
  search.addWidgets([
    customRefinementList({
      // instance params
    })
  ]);

Create a render function

The rendering function is called before the query and each time results come back from Algolia. The function in main.js is populated with rendering options. This code creates the look and feel of the category’s facet values and defines the click-event behavior. It also manages the “show more” logic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 1. Create a render function
const renderRefinementList = (renderOptions, isFirstRender) => {
 const {
   items,
   refine,
   createURL,
   isShowingMore,
   toggleShowMore,
   widgetParams,
 } = renderOptions;
  //Do some initial rendering and bind events
 if (isFirstRender) {
   const ul = document.createElement('ul');
   const button = document.createElement('button');
   button.classList.add('btn-showMore')
   button.textContent = 'Show more';
 
   button.addEventListener('click', () => {
     toggleShowMore();
   });
 
   widgetParams.container.appendChild(ul);
   widgetParams.container.appendChild(button);
 }
 
 //Render the widget
 widgetParams.container.querySelector('ul').innerHTML = items
   .map(
     item => `
       <li style="${isRefined(item)}">
         <a
           href="${createURL(item.value)}"
           data-value="${item.value}"
         >
           ${item.label} (${item.count})
         </a>
       </li>
     `
   )
   .join('');
 
 [...widgetParams.container.querySelectorAll('a')].forEach(element => {
   element.addEventListener('click', event => {
     event.preventDefault();
     refine(event.currentTarget.dataset.value);
   });
 });
 
 const button = widgetParams.container.querySelector('button');
 button.textContent = isShowingMore ? 'Show less' : 'Show more';
};
 
function isRefined(item) {
 if (item.isRefined) {
   return 'font-weight: bold; background-color: rgba(83,101,252, 0.5)'
 }
}

Create the custom refinementList connector

1
2
3
4
// 2. Create the custom widget
const customRefinementList = instantsearch.connectors.connectRefinementList(
  renderRefinementList
);

Instantiate the custom refinementList connector

Finally, you add the new customized connector to your collection of widgets also in main.js. You give it the container used by the HTML, as described in the next section. In the example, the container ID is #guided-search-facets.

1
2
3
4
5
6
7
8
// 3. Instantiate
search.addWidgets([
  customRefinementList({
  document.querySelector('#guided-search-facets')
    attribute: 'categories',
    showMoreLimit: 40,
  })
]);

Update the HTML

Last step is to add the widget to your index.html. Here it’s positioned just under the search bar.

1
2
<div id="searchbox" class="searchbox"></div>
<div id="guided-search-facets" class="guided-search-facets"></div>

Did you find this page helpful?