Integrations / Platforms / Shopify / Customizing InstantSearch

Customizing InstantSearch

Basic customization options are described in the InstantSearch configuration section.

If you want to customize your search beyond what can be done in the configuration section, you’ll need to modify the files we installed into your theme. This requires a certain level of proficiency in JavaScript and CSS.

If you modify any of the files we added to your theme, then you won’t be able to update to the latest version of our plugin without losing your changes because an upgrade overrides your files.

Resources

After you have installed Algolia in your front-end, we load a number of resources into your theme; they fall into three main categories:

  1. External libraries
  2. Generic assets
  3. Plugin specific assets
  4. Pre-bundled InstantSearch widgets

External libraries

External libraries are hosted in your shop theme using the Shopify Assets Management.

We use several libraries to build the search experience we provide for your front end:

  • Hogan.js: Hogan is a templating engine. It has its own section in this documentation.
  • algoliasearch: JavaScript API client for Algolia — needed by autocomplete.js.
  • autocomplete.js: Algolia’s autocompletion library.
  • instantsearch.js: Algolia’s library of widgets that are used to build an InstantSearch page.

In our design, we make use of Font Awesome icons.

Generic assets

We’re installing 5 generic assets in your theme that will be loaded on every page:

File Description
snippets/algolia_money_format.liquid Used to retrieve the currency of your shop
assets/algolia_config.js.liquid Holds your configuration. This file is overwritten on every configuration update in your store, and shouldn’t be edited manually
assets/algolia_init.js.liquid Bootstraps the InstantSearch functionalities for your shop
assets/algolia_helpers.js.liquid Hogan templating logic helpers. See the Hogan helpers section for more details
assets/algolia_translations.js.liquid Contains the string constants used to translate backend results into easily readable strings.

Plugin specific assets

Our styles are split into two CSS files: one for the Autocomplete menu, and one for the InstantSearch page.

Pre-bundled InstantSearch widgets

We pre-bundle the following widgets when you install Algolia on a theme:

Updating the number of facet values shown

To change the number of facet values shown, you need to make two changes:

  1. Ask the Algolia search engine to respond with the required number of facet values.
  2. Update the refinementList widget to show these values.

First, update the algolia_instant_search.js.liquid file to request the correct number of facet values from Algolia (the example assumes you want to show 5 facet values):

1
2
3
4
5
6
7
8
  instant.search.addWidgets([
    configure({
      hitsPerPage: instant.hitsPerPage,
      clickAnalytics: true,
      facetingAfterDistinct: Boolean(algolia.config.show_products),
+     maxValuesPerFacet: 5,
    }),
  ]);

Second, update the algolia_facets.js.liquid file so that the refinementList widget shows all the required values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  var TYPES_TO_WIDGET = {
    slider: { name: 'rangeSlider', useDefault: true, widget: rangeSlider },
    menu: { name: 'menu', params: { limit: 10 }, widget: menu },
    conjunctive: {
      name: 'refinementList',
-     params: { operator: 'and', limit: 10 },
+     params: { operator: 'and', limit: 5 },
      widget: refinementList,
    },
    disjunctive: {
      name: 'refinementList',
-     params: { operator: 'or', limit: 10 },
+     params: { operator: 'or', limit: 5 },
      widget: refinementList,
    },
  };

Add a Show more button to the refinementList widget by setting showMore to true and showMoreLimit to an applicable limit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  var TYPES_TO_WIDGET = {
    slider: { name: 'rangeSlider', useDefault: true, widget: rangeSlider },
    menu: { name: 'menu', params: { limit: 10 }, widget: menu },
    conjunctive: {
      name: 'refinementList',
-     params: { operator: 'and', limit: 10 },
+     params: { operator: 'and', limit: 5, showMore: true, showMoreLimit: 20 },
      widget: refinementList,
    },
    disjunctive: {
      name: 'refinementList',
-     params: { operator: 'or', limit: 10 },
+     params: { operator: 'or', limit: 5, showMore: true, showMoreLimit: 20 },
      widget: refinementList,
    },
  };

Sorting facet values

To sort the facet values, leverage the sortBy attribute of refinementList.

There are 2 ways to modify the sorting behaviour by updating the algolia_facets.js.liquid file:

  1. Sort all conjunctive/disjunctive facets
  2. Sort a particular facets

Sort all conjunctive/disjunctive facets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  var TYPES_TO_WIDGET = {
    slider: { name: 'rangeSlider', useDefault: true, widget: rangeSlider },
    menu: { name: 'menu', params: { limit: 10 }, widget: menu },
    conjunctive: {
      name: 'refinementList',
-     params: { operator: 'and', limit: 10 },
+     params: { operator: 'and', limit: 10, sortBy: ["isRefined", "name:asc"] },
      widget: refinementList,
    },
    disjunctive: {
      name: 'refinementList',
-     params: { operator: 'or', limit: 10 },
+     params: { operator: 'or', limit: 10, sortBy: ["isRefined", "name:asc"] },
      widget: refinementList,
    },
  };

Sort a particular facet

To sort only particular facets, add an entry in the facetSortFunctions method defined in algolia_facets.js.liquid.

Taking an example of sorting the “vendor” facet by “name”:

1
2
3
4
5
6
7
8
9
  algolia.facetSortFunctions = {
    price_range: sortByRefined(function sortRanges(a, b) {
      if (a.name.length === b.name.length) {
        return a.name.localeCompare(b.name);
      }
      return a.name.length - b.name.length;
    }),
+   vendor: ["isRefined", "name:asc"],
  };

The key in the facetSortFunctions object is the name of the facet. You can verify facet names by inspecting the algoliaShopify.config.facets object.

To see other possible sorting options, refer to sortBy attribute.

Displaying search results as a list by default

If you want to change the default display to a list style, make the following small change to snippets/algolia_instant_search.hogan.liquid:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- +'s in the margin represent added lines, -'s represent removed lines -->

@@ -24,15 +24,15 @@
     <div class="ais-search-header">
       <div class="ais-stats-container"></div>
       <div class="ais-change-display">
-        <span class="ais-change-display-block ais-change-display-selected"><i class="fa fa-th-large"></i></span>
-        <span class="ais-change-display-list"><i class="fa fa-th-list"></i></span>
+        <span class="ais-change-display-block"><i class="fa fa-th-large"></i></span>
+        <span class="ais-change-display-list ais-change-display-selected"><i class="fa fa-th-list"></i></span>
       </div>
       <div class="ais-sort">
         Sort by
         <span class="ais-sort-orders-container"></span>
       </div>
     </div>
-    <div class="ais-hits-container ais-results-as-block"></div>
+    <div class="ais-hits-container ais-results-as-list"></div>
   </div>
   <div class="ais-pagination-container"></div>
 </div>

Enabling Algolia’s search without as you type experience

To enable Algolia powered search on your Shopify application without the search as you type experience, do the following:

1. From the dashboard, make sure your theme has the Algolia plugin installed. Please make a duplicate version for testing/beta purposes.

2. From the Search Options tab, enable InstantSearch.

InstantSearch settings

3. Go to the Themes section of your Shopify store and click on Edit code for the theme.

4. Under Assets category, open the algolia_instant_search.js.liquid file.

5. Find the line containing SearchBox widget and add the following parameter searchOnEnterKeyPressOnly: true, as shown below.

Snippet algolia InstantSearch settings

6. Incorporate InstantSearch into your theme. Note that depending on how you’ve customized your theme, you may need to change some of the styling.

Adding a custom widget

You can add custom widgets to InstantSearch by:

1. Importing the widget

2. Using the widget, by configuring it with correct templates

Importing the widget

Using a pre-bundled widget

If the widget you wish to use is pre-bundled, you can directly import it using by referring to algolia.externals within the algolia_instant_search.js.liquid file.

For example, if you want to add a searchable refinementList widget to InstantSearch targeting the vendor attribute, you can import it like this:

1
var refinementList = algolia.externals.widgets.refinementList;

We recommend importing the widget at the top of the file where the other widgets are imported.

Using a non-bundled widget

If the widget you wish to use isn’t pre-bundled, you will need to make a few changes to be able to use it.

Please refer to importing a non-bundled widget section for the relevant steps. Once done, you can continue with “using the widget” section below.

Using the widget

When using the widget, you need to modify its default templates. This is necessary because InstantSearch uses the Hogan templating language to render widgets and results, but on a Shopify store, the delimiters used by Hogan have been changed to square brackets [] instead of the usual curly braces {}.

Here’s an example of how to add the refinementList widget:

1
2
3
4
5
6
7
8
9
10
11
12
instant.search.addWidgets([
  algolia.externals.widgets.refinementList({
    container: document.querySelector('.some-element-class'),
    attribute: 'vendor',
    searchable: true, // Setting searchable to true creates a search input in the refinementList UI
    templates: {
      // Use the Hogan templates utilising `[]` delimiters
      item: algolia.getTemplate('instant_search_facet_item'),
      showMoreText: algolia.getTemplate('instant_search_facet_show_more')
    }
  })
])

You can add this line anywhere after var instant has been declared.

Importing a non-bundled widget

If you need to use a widget that hasn’t been pre-bundled within our default search UI, you need to follow the next three steps:

1. Add all required librarires directly on your store

Edit the layout of your theme and within the <!-- Algolia head --> ... <!-- /Algolia head --> block, add the following lines:

1
2
3
4
5
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.5.1/dist/algoliasearch-lite.umd.js" integrity="sha256-EXPXz4W6pQgfYY3yTpnDa3OH8/EPn16ciVsPQ/ypsjk=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4.8.3/dist/instantsearch.production.min.js" integrity="sha256-LAGhRRdtVoD6RLo2qDQsU2mp+XVSciKRC8XPOBWmofM=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/hogan.js@3.0.2/dist/hogan-3.0.2.min.js" integrity="sha256-jIAAmB65ff5CEFvV6DRfRWjHFwqq+AHeV4le8f8PYp4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/autocomplete.js@0.37.1/dist/autocomplete.min.js" integrity="sha256-YVWQosorZnr6fALvOW9VALYuInld27RkSPkElGBdCaU=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/search-insights@1.6.3/dist/search-insights.min.js" integrity="sha256-8r3eU2ketKjC+f59eAY6ejwSsgPjNY5Ca1yt67nz2TM=" crossorigin="anonymous"></script>

2. Replaced the minified algolia_externals.js with the following code:

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
(function (algolia) {
  "use strict";

  algolia.externals = {
    // Export the required librarires
    Hogan: window.Hogan,
    instantsearch: window.instantsearch,
    algoliasearch: window.algoliasearch,
    autocomplete: window.autocomplete,
    aa: window.AlgoliaAnalytics.default,

    // Export the required widgets
    widgets: {
      rangeSlider: window.instantsearch.widgets.rangeSlider,
      menu: window.instantsearch.widgets.menu,
      refinementList: window.instantsearch.widgets.refinementList,
      searchBox: window.instantsearch.widgets.searchBox,
      stats: window.instantsearch.widgets.stats,
      sortBy: window.instantsearch.widgets.sortBy,
      clearRefinements: window.instantsearch.widgets.clearRefinements,
      panel: window.instantsearch.widgets.panel,
      hits: window.instantsearch.widgets.hits,
      pagination: window.instantsearch.widgets.pagination,
      configure: window.instantsearch.widgets.configure,
      // Define any new widgets here...
    },

    // Export InstantSearch connectors
    connectors: {
      connectCurrentRefinements:
        instantsearch.connectors.connectCurrentRefinements,
    },
  };
})(window.algoliaShopify);

3. Export any new required widgets in algolia_externals.js

For example, if you wish to use the infiniteHits widget, declare it within the widgets property of the externals object like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Export the required widgets
widgets: {
  rangeSlider: window.instantsearch.widgets.rangeSlider,
  menu: window.instantsearch.widgets.menu,
  refinementList: window.instantsearch.widgets.refinementList,
  searchBox: window.instantsearch.widgets.searchBox,
  stats: window.instantsearch.widgets.stats,
  sortBy: window.instantsearch.widgets.sortBy,
  clearRefinements: window.instantsearch.widgets.clearRefinements,
  panel: window.instantsearch.widgets.panel,
  hits: window.instantsearch.widgets.hits,
  pagination: window.instantsearch.widgets.pagination,
  configure: window.instantsearch.widgets.configure,
  // Define any new widgets here...
  infiniteHits: instantsearch.widgets.infiniteHits,
},

You will now be able to refer to this widget in algolia_instant_search.js as:

1
algolia.externals.widgets.infiniteHits

Changing the default sort order

To update the default sort order, you have to make some minor modifications to the code:

  1. Updating the defined sort orders within algolia_sort_orders.js.liquid
  2. Updating the InstantSearch implementation within algolia_instant_search.js.liquid

Before we begin, we need to know the name of the sort order we want to target as it shows up on the storefront along with the index replica which supports that sort order.
For example, if you want to target the alphabetical A-Z sort order, its name is Name and the index replica is shopify_products_title_asc.

Updating the defined sort orders

You need to update the algolia_sort_orders.js.liquid file to ensure that the required sort order comes first within the list.

For search results page

After the algolia.config.sort_orders.forEach section, update the algolia.sortOrders object so that the required sort order comes up first.
Just after closing bracket of algolia.config.sort_orders.forEach, add:

1
2
3
4
// Updating the default sort order to alphabetical a-z
algolia.sortOrders.sort((so1, _) =>
  so1.label === 'Name' ? -1 : 1
);

The file should look like this:

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
  ...

  algolia.config.sort_orders.forEach(function(sort_order) {
    if (
      sort_order.asc &&
      (sort_order.asc.active === true || sort_order.asc.active === '1')
    ) {
      algolia.sortOrders.push({
        value: sort_order_base + '_' + sort_order.key + '_asc',
        label: sort_order.asc.title,
      });
    }

    if (
      sort_order.desc &&
      (sort_order.desc.active === true || sort_order.desc.active === '1')
    ) {
      algolia.sortOrders.push({
        value: sort_order_base + '_' + sort_order.key + '_desc',
        label: sort_order.desc.title,
      });
    }
  });

+ // Updating the default sort order to alphabetical a-z
+ algolia.sortOrders.sort((so1, _) =>
+   so1.label === 'Name' ? -1 : 1
+ );

  ...

For collection pages

Within the if (collection_sort_orders) section, update the algolia.collectionSortOrders object so that the required sort order comes up first.
Just before the closing bracket of if (collection_sort_orders) {, add:

1
2
3
4
// Updating the default sort order to alphabetical a-z
algolia.collectionSortOrders.sort((so1, _) =>
  so1.label === 'Name' ? -1 : 1
);

The file should look like this:

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
  ...

  if (collection_sort_orders) {
    algolia.collectionSortOrders = [
      {
        value: sort_order_base,
        label: '' + algolia.translations.relevance,
      },
    ];

    collection_sort_orders.forEach(function(sort_order) {
      if (
        sort_order.asc &&
        (sort_order.asc.active === true || sort_order.asc.active === '1')
      ) {
        algolia.collectionSortOrders.push({
          value: sort_order_base + '_' + sort_order.key + '_asc',
          label: sort_order.asc.title,
        });
      }

      if (
        sort_order.desc &&
        (sort_order.desc.active === true || sort_order.desc.active === '1')
      ) {
        algolia.collectionSortOrders.push({
          value: sort_order_base + '_' + sort_order.key + '_desc',
          label: sort_order.desc.title,
        });
      }
    });

+   // Updating the default sort order to alphabetical a-z
+   algolia.collectionSortOrders.sort((so1, _) =>
+     so1.label === 'Name' ? -1 : 1
+   );
  }
})(window.algoliaShopify);

Updating the InstantSearch implementation

To ensure that the desired sort order is selected by default, you need to update the algolia_instant_search.js.liquid file.

For search results page

At the top, below the declaration of var collectionPageEnabled, add the following lines:

1
2
3
4
5
6
   var collectionPageEnabled =
     algolia.is_collection_results_page &&
     algolia.config.instant_search_enabled_on_collection;
+  var targetIndexName = collectionPageEnabled
+    ? algolia.config.index_prefix + 'products'
+    : algolia.config.index_prefix + 'products_title_asc';

We also need to update the usage of indexName:

1
2
3
4
5
6
7
8
9
10
         algolia.config.app_id,
         algolia.config.search_api_key
       ),
-      indexName: algolia.config.index_prefix + 'products',
+      indexName: targetIndexName,
       routing: {
-        stateMapping: singleIndex(algolia.config.index_prefix + 'products'),
+        stateMapping: singleIndex(targetIndexName),
       },
       searchFunction: function(searchFunctionHelper) {

For collection pages

At the top, below the declaration of var collectionPageEnabled, add the following lines:

1
2
3
4
5
6
   var collectionPageEnabled =
     algolia.is_collection_results_page &&
     algolia.config.instant_search_enabled_on_collection;
+  var targetIndexName = collectionPageEnabled
+    ? algolia.config.index_prefix + 'products_title_asc'
+    : algolia.config.index_prefix + 'products';

We also need to update the usage of indexName:

1
2
3
4
5
6
7
8
9
10
         algolia.config.app_id,
         algolia.config.search_api_key
       ),
-      indexName: algolia.config.index_prefix + 'products',
+      indexName: targetIndexName,
       routing: {
-        stateMapping: singleIndex(algolia.config.index_prefix + 'products'),
+        stateMapping: singleIndex(targetIndexName),
       },
       searchFunction: function(searchFunctionHelper) {

If you wish to update the default sort order for a specific collection, you can do so by encapsulating the algolia_instant_search.js.liquid changes within a statement conditional on the collection name (available within the variable collectionHandle) or ID (available within the variable algolia.current_collection_id).

Did you find this page helpful?