Guides / Solutions / Ecommerce / Filtering and Navigation

Facets are a great way to let your users refine their search. But sometimes, there are so many facets that it can be hard to find the right ones. One way to solve this problem is to show the most popular facets at the top of the page.

Screenshot of a guided search

In this image, from the live demo, a set of guided search facets appear near the search box. For example, if a user is looking for a computer, clicking Computers & Tablets will update the results on the page and the facet filters to stay in context with the scope of the query. In this case, the brands facet updates to show relevant brands such as HP and Apple. This helps users to refine and improve the relevance of their results. The order of the facet values is based on count but you can adjust it to alphabetical or any custom order that makes sense for your organization.

Before you begin

This tutorial requires InstantSearch.js.

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.

High-level approach

To create guided search facets, you need to create a UI for 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, 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 three-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

Now 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({
    container: document.querySelector('#guided-search-facets'),
    attribute: 'categories',
    showMoreLimit: 40,
  })
]);

Update the HTML

The last step is to add the widget to your index.html. Here it’s positioned just under the search box.

1
2
<div id="searchbox" class="searchbox"></div>
<div id="guided-search-facets" class="guided-search-facets"></div>
Did you find this page helpful?