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:
- External libraries
- Generic assets
- Plugin specific assets
- 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.
- Font Awesome: An open-source icon pack and CSS framework.
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:
rangeSlider
menu
refinementList
searchBox
stats
sortBy
clearRefinements
panel
hits
pagination
configure
Updating the number of facet values shown
To change the number of facet values shown, you need to make two changes:
- Ask the Algolia search engine to respond with the required number of facet values.
- 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:
- Sort all conjunctive/disjunctive facets
- 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.
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.
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:
- Updating the defined sort orders within
algolia_sort_orders.js.liquid
- 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
).