Guides / Building Search UI / Widgets

Creating your own widgets

Creating React InstantSearch widgets is the third layer of our API. Read about the two others possibilities in the what is 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. We’d love to hear about your use case as our mission with our InstantSearch libraries is to provide the best out-of-the-box experience. Don’t hesitate to send us 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 do I need to create widgets?

By creating widgets we mean being able to create completely new widgets with completely new behaviors that are not available today in the existing widgets. Do not create a new widget if all you want is to extend a widget, like redefining the DOM of an existing widget.

This is a very advanced way to use the library, we therefore advise you to only create new widgets when both using widgets and extending widgets have failed to answer your needs.

We tried very hard to make React InstantSearch a pluggable library able to solve most use cases with simple API entries. But we could not plan for everything, we know there will be some cases the current API is not able to 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 us questions. We will be glad to help you.

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 RefinementList 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.

Head over to our community forum if you have any question about creating your own widget.

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 the refine 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 provided SearchParameters, and returns a new SearchParameters. The SearchParameters type is described in the Helper’s documentation.
  • cleanUp(props, searchState): this function is called when a widget is about to unmount in order 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);

Did you find this page helpful?