Customize a React InstantSearch Widget
Highlight and snippet your search results
Search is all about helping users understand the results. This is especially true when using text-based search. When a user types a query in the search box, the results must show why the results match the query. That’s why Algolia implements robust highlighting that lets you display the matching parts of text attributes in the results. On top of that, Algolia implements snippeting to get only the meaningful part of a text when attributes have a lot of content.
This feature is already packaged for you in React InstantSearch, and like most of its features, it comes in two flavors, depending on your use case:
- When using the DOM, you should use widgets
- When using another rendering (such as React Native), use the connector
Highlight and snippet
Highlighting is based on the results and you need to customize the hits
widget to use the Highlighter. The highlight
and the snippet
widgets take two props:
attribute
: the path to the highlighted attribute of the hit (which can be either a string or an array of strings)hit
: a single result object
Notes
- Use the
highlight
widget when displaying the regular value of an attribute. - Use the
snippet
widget when displaying the snippet version of an attribute. To use this widget, the attribute name passed to theattribute
prop must be present in “Attributes to snippet” on the Algolia dashboard or configured asattributesToSnippet
through aset settings
call to the Algolia API.
In this example, a custom hit widget is created for results that have a highlighted name
field. These examples use the mark
tag to highlight. This is a tag specially made for highlighting pieces of text. The default tag is em
, primarily for legacy reasons.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, SearchBox, Hits, Highlight } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const Hit = ({ hit }) => (
<p>
<Highlight attribute="name" hit={hit} tagName="mark" />
</p>
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox />
<Hits hitComponent={Hit} />
</InstantSearch>
);
export default App;
connectHighlight
The connector provides a function to extract the highlighting data from the results. This function takes a single parameter object with three properties:
attribute
: the highlighted attribute namehit
: a single result objecthighlightProperty
: the path to the structure containing the highlighted attribute. The value is either_highlightResult
or_snippetResult
, depending on whether you want to make aHighlight
or aSnippet
widget.
Since those parameters are taken from the context in which the custom component is used, it’s reasonable to have them as props.
Here is an example of a custom Highlight widget. It can be used the same way as the widgets.
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
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, SearchBox, Hits, connectHighlight } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const CustomHighlight = connectHighlight(({ highlight, attribute, hit }) => {
const parsedHit = highlight({
highlightProperty: '_highlightResult',
attribute,
hit
});
return (
<div>
{parsedHit.map(
part => (part.isHighlighted ? <mark>{part.value}</mark> : part.value)
)}
</div>
);
});
const Hit = ({ hit }) => (
<p>
<CustomHighlight attribute="name" hit={hit} />
</p>
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox />
<Hits hitComponent={Hit} />
</InstantSearch>
);
Style your widgets
All widgets under the react-instantsearch-dom
namespace are shipped with fixed CSS class names.
The format for those class names is ais-NameOfWidget-element--modifier
. This follows the naming convention defined by SUIT CSS.
The different class names used by each widget are described on their respective documentation pages. You can also inspect the underlying DOM and style accordingly.
Loading the theme
No CSS is loaded into your page automatically, but there are two themes that you can load manually:
reset.css
satellite.css
It’s strongly recommended that you at least use reset.css to avoid visual side effects caused by the HTML semantics.
The reset
theme CSS is included within the satellite
CSS, so there is no need to import it separately when using the satellite
theme.
Via CDN
The themes are available on jsDelivr:
Unminified:
- https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/reset.css
- https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/satellite.css
Minified:
- https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/reset-min.css
- https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/satellite-min.css
You can either copy paste the content into your own app or use a direct link to jsDelivr:
1
2
3
4
<!-- Include only the reset -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/reset-min.css" integrity="sha256-KvFgFCzgqSErAPu6y9gz/AhZAvzK48VJASu3DpNLCEQ=" crossorigin="anonymous">
<!-- or include the full Satellite theme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@8.5.1/themes/satellite-min.css" integrity="sha256-woeV7a4SRDsjDc395qjBJ4+ZhDdFn8AqswN1rlTO64E=" crossorigin="anonymous">
Using npm and webpack
1
2
npm install instantsearch.css
npm install --save-dev style-loader css-loader
1
2
3
4
// Include only the reset
import 'instantsearch.css/themes/reset.css';
// or include the full Satellite theme
import 'instantsearch.css/themes/satellite.css';
1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
loaders: [
{
test: /\.css$/,
loaders: ['style?insertAt=top', 'css'],
},
],
},
};
Other bundlers
You can use any other module bundler like Browserify or Parcel to load Algolia’s CSS. React InstantSearch doesn’t rely on any specific module bundler or module loader.
Styling icons
You can style the icon colors using the widget class names:
1
2
3
4
.ais-SearchBox-submitIcon path,
.ais-SearchBox-resetIcon path {
fill: red,
}
Translate your widgets
All static text rendered by widgets, such as “Load more”, “Show more” are translatable using the translations
prop on relevant widgets.
This prop is a mapping of keys-to-translation values. Translation values can be either a string
or a function
, as some take parameters.
The different translation keys supported by widgets and their optional parameters are described on their respective documentation page.
With a string
Here’s an example configuring the “Show more” label with a string on a <Menu>
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, Menu } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<Menu
attribute="categories"
showMore={true}
translations={{
showMore: 'Voir plus'
}}
/>
</InstantSearch>
);
With a function
Here’s an example configuring the “Show more” label with a function on a <Menu>
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, Menu } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<Menu
attribute="categories"
showMore={true}
translations={{
showMore(extended) {
return extended ? 'Voir moins' : 'Voir plus';
}
}}
/>
</InstantSearch>
);
Modify the list of items in widgets
Every widget and connector that handles a list of items exposes a transformItems
option. This option is a function that takes the items as a parameter and expects to return the items. You can use this option to sort, filter, and add manual values.
Sorting
This example uses the transformItems
option to order the items by label
in a ascending mode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { orderBy } from 'lodash';
import {
InstantSearch,
SearchBox,
RefinementList,
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox />
<RefinementList
attribute="categories"
transformItems={items => orderBy(items, "label", "asc")}
/>
</InstantSearch>
);
Filtering
This example uses the transformItems
option to filter out items when the count is lower than 150
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
InstantSearch,
SearchBox,
RefinementList,
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox />
<RefinementList
attribute="categories"
transformItems={items =>
items.filter(item => item.count >= 150)
}
/>
</InstantSearch>
);
Add manual values
By default, the values in a RefinementList
or a Menu
are dynamic. This means that the values are updated with the context of the search. This is the expected behavior most of the time, but sometimes you may want to have a static list of values that never change. To achieve this, you can use the connectors.
This example uses the useRefinementList()
connector to display a static list of values. This RefinementList
will always display and only display the items “iPad” and “Printers”.
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
58
59
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
InstantSearch,
SearchBox,
connectRefinementList
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox />
<StaticRefinementList
attribute="categories"
values={[
{ label: 'iPad', value: 'iPad' },
{ label: 'iPhone', value: 'iPhone' },
]}
/>
</InstantSearch>
);
const StaticRefinementList = connectRefinementList(
({ values, currentRefinement, items, refine }) => (
<ul className="ais-RefinementList-list">
{values.map(staticItem => {
const { isRefined } = items.find(
item => item.label === staticItem.label
) || {
isRefined: false,
};
return (
<li key={staticItem.value}>
<label>
<input
type="checkbox"
value={staticItem.value}
checked={isRefined}
onChange={event => {
const value = event.currentTarget.value;
const next = currentRefinement.includes(value)
? currentRefinement.filter(current => current !== value)
: currentRefinement.concat(value);
refine(next);
}}
/>
{staticItem.label}
</label>
</li>
);
})}
</ul>
)
);
Display facets with no matches
Hiding facets when they don’t match a query can be counter-intuitive. However, because of how Algolia handles faceting, you have to rely on workarounds on the frontend to display facets with no hits.
One way of displaying facets with no matches is by caching the results the first time you receive them. Then, if the amount of actual facet hits that Algolia returns is below the limit set, you can append the cached facets to the list.
This solution comes with limitations:
- Facet hits coming from a faceted search (“Search for facet values”) can’t work because Algolia doesn’t return facets that don’t match (the highlighting won’t work on cached items).
- Sorting might need to be applied again in the custom widget because the internal sorting happens before the renderer is called.
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
function uniqBy(items, property) {
const seen = {};
return items.filter(item => {
const val = item[property];
if (seen[val]) {
return false;
} else {
seen[val] = true;
return true;
}
});
}
const indexName = 'instant_search';
const brandAttribute = 'brand';
const brandLimit = 10;
const initialFacets = [];
// On the first load, we store all facets returned by Algolia
// so we can append them to the returned facets when Algolia returns enough.
// We set their `count` to 0 because they'll show up when nothing
// else matches.
// Note that this triggers another query.
searchClient
.searchForFacetValues([
{
indexName,
params: {
facetName: brandAttribute,
facetQuery: '',
maxFacetHits: brandLimit,
},
},
])
.then(([{ facetHits }]) => {
initialFacets.push(
...facetHits.map(facet => ({
...facet,
label: facet.value,
value: facet.value,
isRefined: false,
count: 0,
}))
);
});
// If Algolia doesn't return enough results, we lose track of a
// potentially refined facet.
// For example, if you refine on "Apple", then search for "chromecast",
// "Apple" is no longer returned, and we don't know that it was selected
// based on the initial facets.
// We need to keep track of the last state to reflect the fact that it
// was refined in the UI.
const currentRefinement = new Set();
const CustomRefinementList = connectRefinementList(({ items, refine }) => {
const toggle = item => { // update `currentRefinement` on toggle
currentRefinement.clear();
item.value.forEach(value => currentRefinement.add(value));
// didn't use new Set([...]) because IE 11 doesn't support it.
// ref: http://kangax.github.io/compat-table/es6/#test-Set
refine(item.value);
};
// If a cached facet is already returned by Algolia, we want it to be
// displayed rather than to display its cached value.
// You might need to sort the items again here because the internal
// sorting already happens before `items` is provided by `connectRefinementList`.
const combinedItems = uniqBy([...items, ...initialFacets], 'label')
.slice(0, brandLimit)
.map(item => {
// Here we re-calculate `isRefined` and `value` for each item
// based on `currentRefinement` which we cached at `toggle`.
const isRefined = currentRefinement.has(item.label);
const value =
isRefined === false
? [...currentRefinement, item.label]
: [...currentRefinement].filter(label => label !== item.label);
return {
...item,
isRefined,
value,
};
});
return (
<div>
<ul>
{combinedItems.map((item, index) => (
<li key={index}>
<label>
<input
type="checkbox"
checked={item.isRefined}
onChange={() => toggle(item)}
/>
<span>
{item.label}: {item.count}
</span>
</label>
</li>
))}
</ul>
</div>
);
});
const App = () => (
<InstantSearch indexName={indexName} searchClient={searchClient}>
<CustomRefinementList attribute="brand" />
</InstantSearch>
);
Searching long lists
For some cases, you want to be able to directly search into a list of facet values. This can be achieved using the searchable
prop on widgets like refinement-list
, menu
, or on connectors useRefinementList()
and useMenu()
. To enable this feature, you’ll need to make the attribute searchable using the API or the Dashboard).
With widgets
Use the searchable
prop to add a search box to supported widgets:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
InstantSearch,
SearchBox,
RefinementList
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<RefinementList
attribute="brand"
searchable={true}
/>
</InstantSearch>
);
With connectors
You can implement your own search box for searching for items in lists when using supported connectors by using those provided props:
searchForItems(query)
: call this function with a search query to trigger a new search for itemsisFromSearch
:true
when you are in search mode and the provideditems
are search items results
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
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
InstantSearch,
Highlight,
connectRefinementList
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<RefinementListWithSearchBox attribute="brand" />
</InstantSearch>
);
const RefinementListWithSearchBox = connectRefinementList(
({ items, refine, searchForItems }) => {
const values = items.map(item => {
const label = item._highlightResult ? (
<Highlight attribute="label" hit={item} />
) : (
item.label
);
return (
<li key={item.value}>
<span onClick={() => refine(item.value)}>
{label} {item.isRefined ? "- selected" : ""}
</span>
</li>
);
});
return (
<div>
<input
type="input"
onChange={e => searchForItems(e.currentTarget.value)}
/>
<ul>{values}</ul>
</div>
);
}
);
Apply default value to widgets
A question that comes up frequently is “How to instantiate a refinement-list
widget with a pre-selected item?”. For this use case, you can use the configure
widget.
The following example instantiates a search page with a default query of “apple” and will show a category menu where the item “Cell Phones” is already selected:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, SearchBox, RefinementList, Hits } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<SearchBox defaultRefinement="apple" />
<RefinementList attribute="categories" defaultRefinement="Cell Phones" />
<Hits />
</InstantSearch>
);
Virtual widgets
Many websites have “category pages” where the search context is already refined without the user having to do it. This constrains the search to only the specific results that you want to display. For example, an online shop for electronics devices could have a page like electronics.com/cell-phones
that only shows cell phones:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
connectRefinementList,
Hits,
InstantSearch,
SearchBox,
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const VirtualRefinementList = connectRefinementList(() => null);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<VirtualRefinementList attribute="categories" defaultRefinement="Cell Phones" />
<SearchBox />
<Hits />
</InstantSearch>
);
In this case, VirtualRefinementList
is used with defaultRefinement
to pre-refine results (within the categories
search to only display Cell Phones
). Think of the VirtualRefinementList
as a hidden filter that defines attributes and values that you will always apply to search results.
Hiding default refinements
In some situations, not only do you want default refinements, but you also don’t want the user to be able to un-select them.
By default, the current-refinements
widget or the
useCurrentRefinements()
connector will display the defaultRefinement
. If you want to hide it, you need to filter the items with transformItems
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
InstantSearch,
CurrentRefinements,
connectMenu,
} from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const VirtualMenu = connectMenu(() => null);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<VirtualMenu attribute="categories" defaultRefinement="Cell Phones" />
<CurrentRefinements
transformItems={items =>
items.filter(item => item.currentRefinement !== 'Cell Phones')
}
/>
</InstantSearch>
);
Configure
It might happen that the predefined widgets don’t fit your use case. In that case, you can still apply search parameters by using the configure
widget.
Here’s how you can then preselect a brand without even having to have the underlying RefinementList widget used:
1
2
3
4
5
<InstantSearch searchClient={searchClient} indexName="instant_search">
<Configure filters="brand:Samsung" />
<SearchBox />
<Hits />
</InstantSearch>
Read the filtering guide to understand how filter syntax works and what can you put inside.
How to provide search parameters
Algolia has a wide range of parameters. If one of the parameters you want to use isn’t covered by any widget or connector, you can use the configure
widget.
Here’s an example configuring the distinct parameter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { InstantSearch, Configure } from 'react-instantsearch-dom';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
const App = () => (
<InstantSearch indexName="instant_search" searchClient={searchClient}>
<Configure distinct={1}/>
{/* Widgets */}
</InstantSearch>
);
Notes
- There’s a dedicated guide showing how to configure default refinements on widgets.
- You could also pass
hitsPerPage: 20
to configure the number of hits being shown when not usinginstantsearch
.
Dynamic update of search parameters
Every applied search parameter can be retrieved by listening to the onSearchStateChange
hook from the instantsearch
root component.
But to update search parameters, you will need to pass updated props to the configure
widget. Directly modifying the search state
prop and injecting it will have no effect.
This example shows how to perform geo-searching with React InstantSearch and illustrates how to update search parameters.
Filtering results without widgets
Widgets already provide a lot of different ways to filter your results, but sometimes you might have more complicated use cases that require the usage of the filters
search parameter.
Don’t use filters on a attribute already used with a widget, it will conflict.
1
<Configure filters="NOT categories:"Cell Phones"/>
Customize the complete UI of the widgets
Extending React InstantSearch widgets is the second layer of the API. Read about the two others possibilities in the “What’s InstantSearch?” guide.
This guide explains what you need to know before using connectors (the API feature behind extending widgets use case). Some parts may seem abstract at first, but they will make more sense once you try at least one connector.
When to extend widgets?
‘Extending widgets’ means redefining the rendering output of an existing widget. For instance, if you want to render the Menu widget as an HTML select
element, you need to extend the Menu widget.
Here are some common examples that require the usage of the connectors API:
- When you want to display widgets using another UI library like Material-UI
- When you want to have complete control over the rendering without having to re-implement business logic
- As soon as you hit a feature wall using the default widgets
- When you are a React Native, read the guide on React Native for more information.
How widgets are built
React InstantSearch widgets are built in two parts:
- Business logic code
- Rendering code
The business logic is called connectors
. They’re implemented with higher-order components. They encapsulate the logic for a specific kind of widget, and they provide a way to interact with the InstantSearch context. Those connectors allow you to completely extend existing widgets. The rendering is the React-specific code that tied to each platform (DOM or Native).
Connectors
Connectors are the API implementation Algolia provides for you to extend widgets. Connectors are functions you import and use to get the business logic of a particular widget.
There’s a 1-1 mapping between widgets and connectors: every widget has a connector and vice-versa. This means that you need to use its connector anytime you want to extend a widget.
Connectors for every widget are documented in the API reference, for example the menu
widget.
Connectors render API
The aim is to share as much of a common API between all connectors as possible. Once you know how to use one connector, you can use them all.
As higher-order components, they have an outer component API (called exposed props). They will also provide some other props to the wrapped components - these are called the provided props.
Exposed props
Connectors expose props to configure their behavior. Like the attribute
being refined in a menu
. One common exposed prop that you can use is the defaultRefinement
. Use it to provide the default refinement when the connected component is mounted.
Provided props
Most connectors use the same naming for properties passed down to your components.
items[]
: array of items to display, for example, the brands list of a custom Refinement List. Every extended widget displaying a list gets an item’s property to the data passed to its render function.refine(value|item.value)
: will refine the current state of the widget. Examples include: updating the query for a custom SearchBox or selecting a new item in a custom RefinementList.currentRefinement
: currently applied refinement value (usually the call value of refine()).createURL(value|item.value)
: will return a full URL you can display for the specific refine value given you are using the routing feature.
An item is an object that you will find in items
arrays. The shape of those objects is always the same.
item.value
: The underlying precomputed state value to pass torefine
orcreateURL
item.label
: The label to display, for example, “Samsung”item.count
: The number of hits matching this itemitem.isRefined
: Is the item currently selected as a filter
Some connectors will have more data than others. Read their API reference to know more. Connectors for every widget are documented in the API reference, for example the menu
widget.
Extending widget example
Below is a fully working example of a Menu widget rendered as a select
HTML element.
The files to look at are src/MenuSelect.js
, where you will find the code using connectMenu
. And src/App.js
shows how to use the newly created component.
Head over to the Discord server if you still have questions about extending widgets.
The search state
The state of all widgets is exposed in the searchState
object.
You can learn more about it in the dedicated section ui-state
.
Widgets remain mandatory for applying state to queries. In other words, to apply a refinement, the widget that controls this refinement must be mounted on the page. For example, you can’t apply URL syncing without having a mounted <SearchBox>
component. You can use a Virtual Widget to apply a refinement without having anything rendered on the page.