Migrating from SearchKit to ReactiveSearch
One of the most frequent questions we see from potential ReactiveSearch users is “How to Migrate my existing Search App to use ReactiveSearch”? “Do you support <X>”? In this post, we will cover this question using one of the early and still popular alternative for building Search UIs with ElasticSearch — SearchKit as a reference.
We’ve also written a migration guide from Algolia’s InstantSearch UI to ReactiveSearch. You can read it here.
ReactiveSearch 3.0 is out now. Read all about it over here.
ReactiveSearch is one of the most actively maintained UI libraries out there (we try!), you can see our activity pulse over here. Since our original library for React was released in 2017, we have addressed over 1,000 issues and PRs, seen our users use it in a variety of contexts and released equivalent React Native and Vue.JS versions. We are actively working on bringing variants of ReactiveSearch to all JS frameworks, as well as porting it to Android currently.
🚀 We now also offer a marketplace of boilerplate ReactiveSearch apps — with use-cases varying from building datagrid layouts zto a full-stack e-commerce app to using different design kits with ReactiveSearch.
Follow Along
To make this easy to follow, I will be migrating a BookSearch app from SearchKit to ReactiveSearch.
Live demo of both the versions that can be browsed at the below links:
SearchKit App Link: https://tender-brown-c5d69b.netlify.com/
ReactiveSearch App Link: https://hungry-ramanujan-ac1399.netlify.com/
Setting Up ElasticSearch
Setting up and maintaining our own search engine cluster can be time-consuming and costly. So we will be using Appbase.io for this. At appbase.io, we have also built some dev tools to help you do all these things within a matter of clicks.
- A tool to add data into ElasticSearch — Importer
- A tool to view ElasticSearch data like an excel sheet — Data Browser
- A tool to generate relevant ElasticSearch queries easily — Query Builder
Setting up the backend ⚒
As mentioned above, we will be using an appbase.io hosted ElasticSearch index. In order to make the BookSearch application, we will need a dataset of some really good books. I have already created an appbase.io app with the books dataset indexed over here. You can either clone this dataset to use your own credentials or copy the below credentials to follow along:
url="https://scalr.api.appbase.io/new-book-search"
credentials="vrTi58e8o:04540063-5d81-4fb1-9969-a52d29892995"
I have used these credentials to create a SearchKit application. You can check the codebase for the app here.
Migrating from SearchKit 🚀
I will be going through the migration of this app in an easy to follow along with style and will use CodeSandbox to track each step. I have already set up the initial codesandbox for you in order to make the migration easy. You can check it here.
Structure of the app:
.
├── src
│ ├── index.js Entry Point
│ ├── App.js Renders all the component
│ ├── constant.js Contains Credentials & name
│ └── components
│ ├── Filters.js Render all Filters
│ ├── Results.js Render Result List
│ └── Navbar.js Render Navbar with SearchBar
└── ..
Adding Base Component
SearchKit:
All the SearchKit components are wrapped inside a container called SearchkitProvider which connects the component with elasticsearch index.
We need to create an instance of SearchkitManager and pass it as a prop in SearchkitProvider. Note
We can only have one child in SearchkitProvider
so to accommodate multiple children we need to wrap it with another element.
ReactiveSearch:
All the ReactiveSearch components are wrapped inside a container component — ReactiveBase
which connects the components to an Elasticsearch index. We’ll apply this in the /src/App.js
file. You can read more about Reactivebase
here.
Since we are using the appbase.io app, we just need to pass app name and credentials. We are also using the theme
prop to match the default theme of Searchkit
. You can read more about ReactiveBase
here.
We have connected our ElasticSearch index with the UI using the base component. We can now proceed to add other components.
Integrating Search in Navbar 🔍
We need a search box UI in Navbar to search across various authors & books. We need to add this component in /src/components/Navbar.js
.
SearchKit:SearchBox
is the component which allows users to type their search queries. You can read more about the component here.
ReactiveSearch:DataSearch
is the component which allows users to type their search queries. You can read more about the component here.
- componentId
String
unique identifier of the component. - dataField
String or Array
database field(s) to be connected to the component’s UI view. DataSearch accepts an Array in addition to String, useful for applying search across multiple fields. - fieldWeights
Array
set the search weight for the database fields, useful when dataField is an Array of more than one field. This prop accepts an array of numbers. A higher number implies a higher relevance weight for the corresponding field in the search results. - placeholder
String
set the placeholder text to be shown in the search box input field. Defaults toSearch
. - autosuggest
Boolean
set whether the autosuggest functionality should be enabled or disabled. Defaults totrue
.
When we add this component in Navbar.js
, we will see a Search box appearing on the right side of the navigation bar.
ReactiveSearch
also provides a component called CategorySearch
which can be used to search across various categories. You can read more here.
Displaying books in the Results section 📚
As we now have the search bar in place, we need to now display the books that the user has searched in DataSearch
. We will display books in Result Section that is we need to make changes in src/components/Results.js
.
In SearchKit:Hits
component displays result from ElasticSearch. To customise each result, you need to implement a React component and pass into the itemComponent
prop. The component will receive a single hit
object from the search results, which will include result._source
which contains the untouched stored fields which were indexed.
Pagination
component provides the ability to traverse through pages.
NoHits
component comes handy when there are no results present and can be used to display a message accordingly.
InitialLoader
component is used to show loading status while the data is being fetched from the index.
In the below snippet notice that on Line 3
we are passing another component called ResultCard
. This component just renders the data from ElasticSearch in the card format.
In ReactiveSearch:
ReactiveList
creates a data-driven result UI component. This list can reactively update itself based on changes in other components or changes in the database itself.
We have two other result components along with ReactiveList
— ResultList & ResultCard. In our case, we can make use of the ResultCard
component and render the data from ElasticSearch in card format. You can read more about Results
component here.
Props Used:
- componentId
String
unique identifier of the component, can be referenced in other components’react
prop. - dataField
String
data field to be connected to the component’s UI view. It is useful for providing a sorting context. - pagination
Boolean
Defaults tofalse
. When set totrue
, a pagination based list view with page numbers will appear. - react
allows components to watch each other components and update their data reactively. For example, a Result component can update its data based on the selected fields DataSearch component. We define dependency usingcomponentId
. You can read more about thereact
prop over here.
Note:
You can also use renderItem
& render
prop here to render the UI. Learn more about this props here.
Integrating different facets in the filters section 📙
We now have all the books listed in the Result section, now we can add different facets to filter out the books. Both SearchKit
& ReactiveSearch
provides a number of facets to be used, here we will be taking a look at few of them.
Multiple selection based list Filter:
In SearchKit:RefinementList
lets the user refines the search results. You can specify if you want filters to be OR’ed or AND’ed. For example, if you filter on a and b with OR, results with either the value a or b will match.
In ReactiveSearch:MultiList
creates multiple selections based list UI component that is connected to a database field. MultiList
component is similar to RefinementListFilter
as you can see in the below snippet.
Props used:
- componentId
String
unique identifier of the component can be referenced in other components’react
prop. - dataField
String
data field to be connected to the component’s UI view. This field is used for doing aggregation and returns the result. - title
String or JSX
title of the component to be shown in the UI. Defaults to no title being shown. - queryFormat
String
queries the selected items from the list in one of two modes:or
,and
. - showSearch
Boolean
whether to show a search box to filter the list items locally. Defaults to true.
You can learn about more props here.
Range slider based Filter:
In SearchKit:RangeFilter
lets users filter results within a numerical range. Provides a histogram to show the user where results are found on the range. Read more here.
In ReactiveSearch:RangeSlider
creates a numeric range slider UI component. It is used for granular filtering of numeric data. There is not much code difference except in ReactiveSearch min
& max
are defined in the range
prop.
Props Used:
- componentId
String
unique identifier of the component can be referenced in other components’react
prop. - dataField
String
DB data field to be mapped with the component’s UI view. The selected range creates a database query on this field. - range
Object
an object withstart
andend
keys and corresponding numeric values denoting the minimum and maximum possible slider values. - rangeLabels
Object
an object withstart
andend
keys and correspondingString
labels to show labels near the ends of theRangeSlider
component. - title
String or JSX
title of the component to be shown in the UI.
Numeric range selection Filter:
In SearchKit:NumericRefinementList
allows the user to refine results based on a numerical ElasticSearch field. You can specify an array of options for the user to select from. Will only allow the user to select one.
In ReactiveSearch:SingleRange
creates a numeric range selector UI component that is connected to a database field.
- componentId
String
unique identifier of the component can be referenced in other components’react
prop. - dataField
String
data field to be connected to the component’s UI view. The range items are filtered by a database query on this field. - data
Object Array
collection of UIlabels
with associatedstart
andend
range values. - showRadio
Boolean
show radio button icon for each range item. Defaults totrue
. - showFilter
Boolean
show the selected item as a filter in the selected filters view. Defaults totrue
. - title
String or JSX
title of the component to be shown in the UI.
Note: In order to update Results reactively according to the filter, we will need to add them in the react prop of ReactiveList
.
Adding Selected Filter ✅
Now we have the search app ready, but we want our users to know which filters are applied. We will show these filters above result items.
SelectedFilters
is the component that is used in both SearchKit
and ReactiveSearch
to create a selectable filter UI view displaying the currently selected values from other components. This component is useful for improving selection accessibility of other components.
Adding this component in src/components/Results.js
:
At Line 2 we have introduced the filters component. You can read more about this component here.
With the addition of SelectedFilters
, we have completely migrated the SearchKit
app to ReactiveSearch
app.
Styling 🖌
SearchKit
uses BEM concepts to style a component whereas ReactiveSearch
provide a prop called innerClass
to inject class name to different sub-components. ReactiveSearch also provides theming support via the theme prop.
Porting Custom UI Components 📈
ReactiveSearch is designed to support custom UI components built with an external design kit. ReactiveComponent allows you to use any component and still take advantage of ReactiveSearch’s state management and declarative props.
I hope this post has been helpful to you in understanding how a potential migration from SearchKit
to ReactiveSearch
can work out.
Cheers! 🥂