Guides / Building Search UI / Widgets

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

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 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?