Create Your Own React InstantSearch Widgets
This version of React InstantSearch has been deprecated in favor of the latest version of React InstantSearch.
Creating your own widgets
Creating React InstantSearch widgets is the third layer of Algolia’s API. Read about the two others possibilities in the React InstantSearch guide.
You are trying to create your own widget with React InstantSearch and that’s awesome but that also means that you couldn’t find the widgets or built-in options you were looking for. Algolia would love to hear about your use case as the aim of the InstantSearch libraries is to provide the best out-of-the-box experience. Don’t hesitate to send a quick message explaining what you were trying to achieve either using the form at the end of that page or directly by submitting a feature request.
When to create widgets?
You can create completely new widgets with new behaviors that aren’t available in the existing widgets. Don’t create a new widget if all you want is to extend a widget, like redefining the DOM of an existing widget.
React InstantSearch is a pluggable library that can solve most use cases with simple API entries. But it can’t solve everything, there will be some cases where the API can’t fulfill this promise of simplicity.
If that’s not the case, or you are in doubt, before diving into custom connectors please come to Discourse or GitHub first, where you can explain your situation and ask questions.
Quick start
You can use create-instantsearch-app
. Similar to other interactive command-line applications, you can run it either with npx
or with yarn
:
1
2
3
npx create-instantsearch-app@latest --template 'React InstantSearch widget' 'my-widget'
# or
yarn create instantsearch-app "--template 'React InstantSearch widget' 'my-widget'"
How to create a new widget
To create new widgets, the process is the same as for extending widgets, but instead of reusing an existing connector you would create your own connector. To do that you will need to create your own connector via the createConnector
function.
To do that, the best way is to read how connectors are actually built, have a look at the useRefinementList()
code in the React InstantSearch project. You can also check the example at the bottom of this page. It’s an implementation of a custom connector commented to help you understand the API.
Lifecycle and API
The function createConnector
accepts an object that takes several attributes. You can find below the list of attributes used in this guide (the full list of attributes is available in the API reference).
displayName
: name that will be applied on the higher-order component.getProvidedProps(props, searchState, searchResults)
: this function should return the props to forward to the composed component. It takes in the current props of the higher-order component, the search state of all widgets, the results of the search.refine(props, searchState, ...args)
: this function defines exactly how therefine
prop of widgets affects the search state. It takes in the current props of the higher-order component, the search state of all widgets, as well as all arguments passed to the refine and createURL props of stateful widgets, and returns a new state.getSearchParameters(searchParameters, props, searchState)
: this function applies the current props and state to the providedSearchParameters
, and returns a newSearchParameters
. TheSearchParameters
type is described in the Helper’s documentation.cleanUp(props, searchState)
: this function is called when a widget is about to unmount to clean the searchState. It takes in the current props of the higher-order component and the searchState of all widgets and expects a new searchState in return.
Usage
Here is example that creates a connector which allows a component to update the current query.
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
import { createConnector } from 'react-instantsearch-dom';
const connectWithQuery = createConnector({
displayName: 'WidgetWithQuery',
getProvidedProps(props, searchState) {
// Since the `attributeForMyQuery` searchState entry isn't
// necessarily defined, we need to default its value.
const currentRefinement = searchState.attributeForMyQuery || '';
// Connect the underlying component with the `currentRefinement`
return { currentRefinement };
},
refine(props, searchState, nextRefinement) {
// When the underlying component calls its `refine` prop,
// we update the searchState with the provided refinement.
return {
// `searchState` represents the search state of *all* widgets. We need to extend it
// instead of replacing it, otherwise other widgets will lose their respective state.
...searchState,
attributeForMyQuery: nextRefinement,
};
},
getSearchParameters(searchParameters, props, searchState) {
// When the `attributeForMyQuery` state entry changes, we update the query
return searchParameters.setQuery(searchState.attributeForMyQuery || '');
},
cleanUp(props, searchState) {
// When the widget is unmounted, we omit the entry `attributeForMyQuery`
// from the `searchState`, then on the next request the query will
// be empty
const { attributeForMyQuery, ...nextSearchState } = searchState;
return nextSearchState;
},
});
const MySearchBox = ({ currentRefinement, refine }) => (
<input
type="input"
value={currentRefinement}
onChange={e => refine(e.currentTarget.value)}
/>
);
const ConnectedSearchBox = connectWithQuery(MySearchBox);