Autocomplete
editAutocomplete
editWhen you start typing in a search box on ecommerce sites like Amazon or Best Buy, you might have seen a dropdown showing suggestions to try next. This is called autocomplete.
On some of the more popular ecommerce sites, you may see suggestions in the form of search suggestions, product categories, and products.
This article will discuss how to implement an autocomplete search box that provides multiple options on your ecommerce site.
Search UI can be used to provide different types of suggestions from multiple sources. For example, depending on your use case, you can display query suggestions, products from the product catalog, and a list of popular queries at the same time.
The code in this article examples powers the following demo. Try typing "monitor" in the search box!
Before you begin
editThis tutorial builds on the searchbox autocomplete documentation.
Integrating the Searchbox
editTypically ecommerce sites have a search bar within the site’s header, which is accessible on every page.
For this tutorial, we will assume that the search bar is located in the site’s header.
Within the header, we need to use the SearchProvider
and Searchbox
components to integrate the autocomplete search box.
const config = { // search provider configuration for autocomplete } function NavigationHeader() { return ( <div> <ComponyLogo /> <BrowseNavigationLinks /> <SearchProvider config={{ ...config, trackUrlState: false }} > <SearchBox onSubmit={(searchTerm) => { window.location.href = `${PATH_TO_YOUR_SEARCH_PAGE}?q=${searchTerm}`; }} /> </SearchProvider> <UserProfile /> </div> ); }
Should you find the search bar markup too limiting to your needs, you can override the display using the optional inputView
autocompleteView
and resultsView
props.
<SearchBox autocompleteView={({ autocompletedResults, getItemProps }) => ( <div className="sui-search-box__autocomplete-container"> {autocompletedResults.map((result, i) => ( <div {...getItemProps({ key: result.id.raw, item: result })} className="flex" > Result {i}: {result.title.snippet} </div> ))} </div> )} />
For more information of whats possible to customise, see searchbox autocomplete documentation.
Term Suggestions
editTerm Suggestions help the customer quickly type in the search term. The suggestions are based on keywords that are already present in the index. To do this, you need:
- An engine or index populated with products
To configure the SearchBox to provide suggestions based on keywords, you need to pass a config
object to the SearchProvider
component and configure the Searchbox autocompleteSuggestions
to be true.
Example Code
const config = { autocompleteQuery: { resultsPerPage: 5, result_fields: { name: { snippet: { size: 100, fallback: true }}, url: { raw: {} } }, search_fields: { "name_product_autocomplete": {} } }, };
<SearchBox autocompleteResults={{ titleField: "name", urlField: "url" }} />;
Product Suggestions
editWith this feature, products will be presented as suggestions to the customer. When the customer clicks on the product suggestion, they will be navigated straight to the product’s detail page.
First, we specify the autocompleteQuery.results
configuration:
const config = { alwaysSearchOnInitialLoad: false, autocompleteQuery: { results: { resultsPerPage: 5, result_fields: { // specify the fields you want from the index to display the results image: { raw: {} }, name: { snippet: { size: 100, fallback: true } }, url: { raw: {} } }, search_fields: { // specify the fields you want to search on name: {} } } }, apiConnector: connector };
<SearchBox autocompleteResults={{ titleField: "name", urlField: "url" }} autocompleteView={AutocompleteView} />;
function AutocompleteView({ autocompleteResults, autocompletedResults, autocompleteSuggestions, autocompletedSuggestions, className, getItemProps, getMenuProps }) { let index = 0; return ( <div {...getMenuProps({ className: ["sui-search-box__autocomplete-container", className].join( " " ) })} > <> {!!autocompleteSuggestions && Object.entries(autocompletedSuggestions).map( ([suggestionType, suggestions]) => { return ( <React.Fragment key={suggestionType}> {getSuggestionTitle( suggestionType, autocompleteSuggestions ) && suggestions.length > 0 && ( <div className="sui-search-box__section-title"> {getSuggestionTitle( suggestionType, autocompleteSuggestions )} </div> )} {suggestions.length > 0 && ( <ul className="sui-search-box__suggestion-list"> {suggestions.map((suggestion) => { index++; return ( <li {...getItemProps({ key: suggestion.suggestion || suggestion.highlight, index: index - 1, item: suggestion })} data-transaction-name="query suggestion" > {suggestion.highlight ? ( <span dangerouslySetInnerHTML={{ __html: suggestion.highlight }} /> ) : ( <span>{suggestion.suggestion}</span> )} </li> ); })} </ul> )} </React.Fragment> ); } )} {!!autocompleteResults && !!autocompletedResults && typeof autocompleteResults !== "boolean" && autocompletedResults.length > 0 && autocompleteResults.sectionTitle && ( <div className="sui-search-box__section-title"> {autocompleteResults.sectionTitle} </div> )} {!!autocompleteResults && !!autocompletedResults && autocompletedResults.length > 0 && ( <ul className="flex flex-col w-[300px]"> {autocompletedResults.map((result) => { index++; const titleField = typeof autocompleteResults === "boolean" ? null : autocompleteResults.titleField; const titleRaw = getRaw(result, titleField); return ( <li {...getItemProps({ key: result.id.raw, index: index - 1, item: result })} className="mb-2 flex space-x-5" > <img className="m-auto flex-shrink-0 max-w-[30px]" src={result.image.raw} /> <h5 className="flex-1 text-sm">{titleRaw}</h5> </li> ); })} </ul> )} </> </div> ); }
Suggestions from another source index
editRequires Elasticsearch Connector.
Sometimes you want to display suggestions from a different index than the one you use for search. For example, you might want to show suggestions from a popular_queries
or a designers
index. Search UI supports this within the autocompleteSuggestions
configuration.
In this example, we will populate an index with popular queries. The mapping and example documents for the index will be as follows:
{ "popular_queries" : { "mappings" : { "properties" : { "name" : { "type" : "text", "fields" : { "suggest" : { "type" : "search_as_you_type", "doc_values" : false, "max_shingle_size" : 3 } } } } } } }
{ "name" : "Iphone 4s" }
Next, setup Search UI Searchbox
and configuration
to display suggestions from the popular_queries
index.
const config = { alwaysSearchOnInitialLoad: false, autocompleteQuery: { suggestions: { types: { popularQueries: { search_fields: { "name.suggest": {} // fields used to query }, result_fields: { name: { raw: {} } }, index: "popular_queries", queryType: "results" } }, size: 4 }, }, apiConnector: connector };
<SearchBox autocompleteSuggestions={{ popularQueries: { sectionTitle: "Popular queries", queryType: "results", displayField: "name" } }} />;
Now, when you type was
in the SearchBox, the autocomplete view will display the popular queries:
If you want to display more fields from the index, you can use the result_fields
configuration and implement a custom autocompleteView
to display these fields.
Suggestions from multiple sources
editCombining the suggestion configurations above allows you to display suggestions from multiple sources simultaneously.
To do this, extend the autocompleteQuery
configuration to specify multiple sources. For example, in the screenshot above, we customized the autocompleteView
CSS to display the popular queries and the results from the autocompleteSuggestions
configuration side by side and hide the section titles.
const config = { alwaysSearchOnInitialLoad: false, autocompleteQuery: { suggestions: { types: { popularQueries: { search_fields: { "name.suggest": {} // fields used to query }, result_fields: { name: { raw: {} } }, index: "popular_queries", queryType: "results" } }, size: 4 }, }, apiConnector: connector };
<SearchBox autocompleteResults={{ sectionTitle: "Products", titleField: "name", urlField: "url" }} autocompleteSuggestions={{ popularQueries: { sectionTitle: "Popular queries", queryType: "results", displayField: "name" } }} />