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 in the theme. It’s best if you work together with a frontend developer to customize the InstantSearch results page.
If you modify any of Algolia’s files in your theme, you won’t be able to update to the latest version of the app without losing your changes because an upgrade overrides your Algolia files.
Resources
The Algolia Search and Discovery app adds a few additional resources to your theme.
External libraries
External libraries are hosted in your shop theme using the Shopify Assets Management. The Algolia Search and Discovery app uses several libraries to build the search experience for your frontend:
- Algolia JavaScript API client. Needed by autocomplete.js.
- Autocomplete. Algolia’s autocompletion library.
- InstantSearch.js. Algolia’s library of widgets that are used to build an InstantSearch page.
- Font Awesome: An open source icon pack and CSS framework.
Generic assets
The following assets are 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 InstantSearch for your shop |
assets/algolia_helpers.js.liquid |
Templating logic helpers. For more information, see Available helpers section |
assets/algolia_translations.js.liquid |
Contains the string constants used to translate backend results into easily readable strings |
Plugin specific assets
The CSS styles are split into two files:
- Styles for the autocomplete menu
- Styles for the InstantSearch page
Pre-bundled InstantSearch widgets
The Algolia Search and Discovery app includes these InstantSearch widgets:
rangeSlider
menu
refinementList
searchBox
stats
sortBy
clearRefinements
panel
hits
pagination
configure
Update the number of facet values
To change the number of facet values shown on the search results page, you need to make two changes:
-
Request the correct number of facet values from Algolia. Update the file
algolia_instantsearch.js.liquid
. For example, to retrieve five facet values from the search engine:Copy1 2 3 4 5 6 7
instant.search.addWidgets([ configure({ hitsPerPage: instant.hitsPerPage, facetingAfterDistinct: Boolean(algolia.config.show_products), + maxValuesPerFacet: 5, }), ]);
-
Update the
algolia_facets.js.liquid
file so that therefinementList
widget shows all the required values:Copy1 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, }, };
To let users see more facet values than you initially show in your interface,
add a Show more button to the refinementList
widget.
Set showMore
to true
and set 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, set the sortBy
attribute of the refinementList
widget.
Sort all conjunctive/disjunctive facets
Update the algolia_facets.js.liquid
file to sort all conjunctive or 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
.
For example, to sort 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 facet name.
You can find the facet names in the algoliaShopify.config.facets
object.
To see other possible sorting options, see sortBy
.
Display search results as a list by default
To display search results as a list, update the template snippets/algolia_instant_search.liquid
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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">
<!-- ... -->
</div>
- <div class="ais-hits-container ais-results-as-block"></div>
+ <div class="ais-hits-container ais-results-as-list"></div>
<div class="ais-pagination-container"></div>
</div>
Turn off search-as-you-type
To use Algolia Search without the search-as-you-type experience, follow these steps:
-
In your Shopify admin, make sure that your theme has the Algolia Search and Discovery app installed. Duplicate the theme for testing or staging.
-
In the Search Options tab, enable InstantSearch:
-
In the Themes section of your Shopify store, select your theme and click Edit code.
-
Under Assets, open the
algolia_instant_search.js.liquid
file. -
Find the code for the
searchBox
widget and addsearchAsYouType: false
to its configuration: -
Add InstantSearch to your theme. Depending on how you customized your theme, you might need to adjust the styles.
Add custom widgets
You can add custom widgets to InstantSearch, either by importing a custom widget or by configuring a widget with custom templates.
Import pre-bundled widgets
Pre-bundled widgets are included by default.
You can import pre-bundled widgets from the algolia.externals
object within the algolia_instant_search.js.liquid
file.
For example, to add a refinementList
widget:
1
var refinementList = algolia.externals.widgets.refinementList;
It’s best to keep the imports together at the top of the file.
Import non-bundled widgets
To import a widget that isn’t included in the Algolia Search and Discovery app, follow these steps:
First, add all libraries directly to your store,
Edit your theme.liquid
file and add the following lines between the <!-- Algolia head --> ... <!-- /Algolia head -->
block:
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.9.0/dist/search-insights.min.js" integrity="sha256-/zXpGP2+kWLFmVvxDBsJtOhx05CPGoY6nuOg9P/5fgc=" crossorigin="anonymous"></script>
Then, replace the file 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);
Next, export any new widgets in algolia_externals.js
.
For example, to use the infiniteHits
widget,
add it to the widgets
property of the externals
object:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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,
+ infiniteHits: instantsearch.widgets.infiniteHits,
},
Now you can refer to this widget in algolia_instant_search.js
:
1
algolia.externals.widgets.infiniteHits
Use the widgets
When using the widget, you can modify its default templates.
If you aren’t using the latest version of the Algolia Search and Discovery InstantSearch widget,
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.
Change the default sort order
If you want to change the sort order, you need to know:
- The name of the sort order as it shows up in your store front
- The name of the replica index for this sort order
For example, to target the alphabetical A-Z sort order, its name is Name and the replica index is shopify_products_title_asc
.
To update the default sort order, you have to make some minor modifications to the code:
- Update the defined sort orders within
algolia_sort_orders.js.liquid
- Update the InstantSearch implementation within
algolia_instant_search.js.liquid
Update the defined sort orders
Update the algolia_sort_orders.js.liquid
file to ensure that the required sort order comes first within the list.
For the search results page
In the algolia.config.sort_orders
section,
update the algolia.sortOrders
object so that the required sort order comes first:
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
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
In the if (collection_sort_orders)
section,
update the algolia.collectionSortOrders
object so that the required sort order comes up first:
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
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);
Update the InstantSearch implementation
To ensure that the desired sort order is selected by default,
update the algolia_instant_search.js.liquid
file.
For the search results page
Add the following lines to the top of algolia_instant_search.js.liquid
:
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';
You need to update the indexName
as well:
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
Add the following lines to the top of algolia_instant_search.js.liquid
:
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';
Update the indexName
as well:
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) {
To update the default sort order for a specific collection,
you can encapsulate the changes in algolia_instant_search.js.liquid
within a statement conditional on the collection names
(available within the variable collectionHandle
) or ID
(available within the variable algolia.current_collection_id
).