Upgrading React InstantSearch Hooks
On this page
Migrating from React InstantSearch to React InstantSearch Hooks
React InstantSearch Hooks and React InstantSearch are separate, incompatible libraries.
React InstantSearch is a standalone library with ready-to-use UI components. React InstantSearch Hooks is based on InstantSearch.js, and lets you fully control the rendering. With React InstantSearch Hooks, you can create your own UI components using React Hooks.
This guide helps you migrate from React InstantSearch v6 to React InstantSearch Hooks v6. If you’re using React InstantSearch < 6, please upgrade to v6 first.
Components
React InstantSearch Hooks provides most of the same UI components as React InstantSearch. When no UI component is available, you can use the matching Hooks to build it yourself.
React InstantSearch | React InstantSearch Hooks |
---|---|
<Breadcrumb> |
<Breadcrumb> |
<ClearRefinements> |
<ClearRefinements> |
<Configure> |
<Configure> |
<CurrentRefinements> |
<CurrentRefinements> |
<DynamicWidgets> |
<DynamicWidgets> |
<EXPERIMENTAL_Answers> |
useConnector(connectAnswers) (no UI component) |
EXPERIMENTAL_useAnswers() |
useConnector(connectAnswers) (no UI component) |
<ExperimentalConfigureRelatedItems> |
useConnector(connectConfigureRelatedItems) (no UI component) |
<HierarchicalMenu> |
<HierarchicalMenu> |
<Highlight> |
<Highlight> |
<Hits> |
<Hits> |
<HitsPerPage> |
<HitsPerPage> |
<Index> |
<Index> |
<InfiniteHits> |
<InfiniteHits> |
<InstantSearch> |
<InstantSearch> |
<Menu> |
<Menu> |
<MenuSelect> |
useMenu() (no UI component) |
<NumericMenu> |
useConnector(connectNumericMenu) (no UI component) |
<Pagination> |
<Pagination> |
<Panel> |
No equivalent available |
<PoweredBy> |
<PoweredBy> |
<QueryRuleContext> |
useQueryRules() (no UI component) |
<QueryRuleCustomData> |
useQueryRules() (no UI component) |
<RangeInput> |
<RangeInput> |
<RangeSlider> |
useRange() (no UI component) |
<RatingMenu> |
useMenu() (no UI component) |
<RefinementList> |
<RefinementList> |
<RelevantSort> |
useConnector(connectRelevantSort) (no UI component) |
<ScrollTo> |
No equivalent available |
<SearchBox> |
<SearchBox> |
<Snippet> |
<Snippet> |
<SortBy> |
<SortBy> |
<Stats> |
No equivalent available |
<ToggleRefinement> |
<ToggleRefinement> |
<VoiceSearch> |
useConnector(connectVoiceSearch) (no UI component) |
Component props differ slightly between React InstantSearch and React InstantSearch Hooks. Refer to the API reference of each component for information.
Menu
The <Menu>
widget in React InstantSearch Hooks isn’t searchable. You can create a searchable version by using a custom widget with the useRefinementList()
connector.
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
import {
useRefinementList,
useInstantSearch,
} from 'react-instantsearch-hooks-web';
function Menu({ attribute, ...props }) {
const { setIndexUiState } = useInstantSearch();
const { items, searchForItems, isFromSearch } = useRefinementList({
attribute,
});
const [query, setQuery] = React.useState('');
function refine(value: string) {
setQuery('');
setIndexUiState((uiState) => ({
...uiState,
refinementList: {
...uiState.refinementList,
[attribute]: [value],
},
}));
}
return (
<div {...props}>
<input
type="search"
value={query}
onChange={(event) => {
const nextValue = event.target.value;
setQuery(nextValue);
searchForItems(nextValue);
}}
/>
<ul>
{items.map((item) => (
<li key={item.label}>
<label>
<input
type="radio"
checked={item.isRefined}
onChange={() => refine(item.value)}
/>
{isFromSearch ? (
<Highlight hit={mapToHit(item)} attribute="highlighted" />
) : (
item.label
)}
</label>
</li>
))}
</ul>
</div>
);
}
function mapToHit(item: RefinementListItem): AlgoliaHit<RefinementListItem> {
return {
...item,
_highlightResult: {
highlighted: {
value: item.highlighted,
matchLevel: 'full',
matchedWords: [],
},
},
__position: 0,
objectID: '',
};
}
Connectors
React InstantSearch Hooks provides the same connectors as React InstantSearch except the connectStateResults
connector, which isn’t available in React InstantSearch Hooks.
connectStateResults
React InstantSearch Hooks doesn’t have the connectStateResults
connector.
You can use the useInstantSearch()
hook instead.
Using connectors and hooks
React InstantSearch Hooks is a bridge between InstantSearch.js connectors and React Hooks. The connectors from React InstantSearch and InstantSearch.js were historically different APIs, meaning there are differences between the props that React InstantSearch connectors accept and the new Hooks.
Refer to the API reference to see the new props.
The React InstantSearch Hooks package uses TypeScript natively. If your editor supports code completion (IntelliSense), you can use it to discover the new props.
Creating connectors
The connector API has changed to use InstantSearch.js connectors. The previous createConnector()
function is no longer available.
React InstantSearch Hooks works with all InstantSearch.js connectors—official Algolia connectors, and community ones.
To create your own Hook, you can use an existing connector or create your InstantSearch.js connector.
Routing
Routing now follows the InstantSearch.js routing APIs with the <InstantSearch>
routing prop.
Server-side rendering (SSR)
The server APIs have been simplified in React InstantSearch Hooks.
Replace findResultsState()
with getServerState()
.
This new API accepts an element <App />
. You can pass props directly to <App />
.
In your server code, you don’t need to provide your index name and your search client anymore, because they’re already in your <App>
.
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
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
-import { findResultsState } from 'react-instantsearch-dom/server';
+import { getServerState } from 'react-instantsearch-hooks-server';
import App from './App';
-const indexName = 'instant_search';
-const searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const app = express();
app.get('/', async (req, res) => {
- const searchState = { /* ... */ };
- const serverState = {
- resultsState: await findResultsState(App, {
- searchClient,
- indexName,
- searchState,
- }),
- searchState,
- };
+ const serverState = await getServerState(<App />);
const html = renderToString(<App serverState={serverState} />);
res.send(
`
<!DOCTYPE html>
<html>
<head>
<script>window.__SERVER_STATE__ = ${JSON.stringify(serverState)};</script>
</head>
<body>
<div id="root">${html}</div>
</body>
<script src="/assets/bundle.js"></script>
</html>
`
);
});
app.listen(8080);
You don’t need to pass any props to <InstantSearch>
to support SSR, only wrap the component to render on the server with <InstantSearchSSRProvider>
. Then, pass the server state by spreading the getServerState()
prop.
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
import algoliasearch from 'algoliasearch/lite';
import React from 'react';
-import { InstantSearch } from 'react-instantsearch-dom';
+import {
+ InstantSearch,
+ InstantSearchSSRProvider,
+} from 'react-instantsearch-hooks-web';
const searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
function App(props) {
return (
+ <InstantSearchSSRProvider {...props.serverState}>
<InstantSearch
indexName="instant_search"
searchClient={searchClient}
- resultsState={props.serverState.resultsState}
- onSearchParameters={props.onSearchParameters}
- widgetsCollector={props.widgetsCollector}
>
{/* Widgets */}
</InstantSearch>
+ </InstantSearchSSRProvider>
);
}
export default App;
Types
Types are now available in React InstantSearch Hooks.
You can uninstall all InstantSearch types coming from DefinitelyTyped.
1
2
3
yarn remove @types/react-instantsearch-dom @types/react-instantsearch-core @types/react-instantsearch
# or
npm uninstall @types/react-instantsearch-dom @types/react-instantsearch-core @types/react-instantsearch
Default refinement
In React InstantSearch, every widget has a defaultRefinement
prop. This doesn’t exist in React InstantSearch hooks and is replaced with initialUiState
.
Controlled mode
The props searchState
and onSearchStateChange
are equivalent to the onStateChange
prop.
Upgrade event tracking
Starting from v6.43.0, React InstantSearch Hooks simplifies the event tracking process via the insights
option. You no longer need to install the search-insights
library or set up the insights
middleware yourself.
Here are some benefits when using the insights
option:
- It better handles the
userToken
. Once you set it, all the search and event tracking calls include the token. - It automatically sends default events from built-in widgets such as
<RefinementList>
,<Menu>
, etc. You can also change the event payloads, or remove them altogether. - It lets you send custom events from your custom widgets.
- It simplifies forwarding events to third-party trackers.
If you’ve been tracking events directly with search-insights
or with the insights
middleware, you should:
- Upgrade
react-instantsearch-hooks
to v6.43.0 or greater - Migrate from using the
insights
middleware to theinsights
option - Either update or remove the
search-insights
library
Use the insights
option
Starting from v6.43.0, InstantSearch lets you enable event tracking with the insights
option. You no longer need to set up the insights
middleware yourself.
1
2
3
4
5
6
7
8
9
10
function App() {
return (
<InstantSearch
// ...
insights={true}
>
{/* Widgets */}
</InstantSearch>
);
}
If you had already set up the insights
middleware in your code, you can now remove it and move its configuration to the insights
option.
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
-import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
-import { useInstantSearch } from 'react-instantsearch-hooks-web';
-import { useLayoutEffect } from 'react';
import algoliasearch from 'algoliasearch/lite';
- function InsightsMiddleware() {
- const { use } = useInstantSearch();
-
- useLayoutEffect(() => {
- const middleware = createInsightsMiddleware({
- insightsClient: window.aa,
- insightsInitParams: {
- useCookie: false,
- }
- });
-
- return use(middleware);
- }, [use]);
-
- return null;
-}
function App() {
return (
<InstantSearch
// ...
+ insights={{
+ insightsInitParams: {
+ useCookie: false,
+ }
+ }}
>
<InsightsMiddleware />
{/* Widgets */}
</InstantSearch>
);
}
Update search-insights
Starting from v4.55.0, InstantSearch can load search-insights
for you so the insightsClient
option is no longer required.
If you prefer loading it yourself, make sure to update search-insights
to v2.4.0 and forward the reference to insights
.
If you’re using the UMD bundle with a <script>
tag, make sure to update the full code snippet (not just the version):
1
2
3
4
5
6
7
8
<script>
var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@2.6.0/dist/search-insights.min.js";
!function(e,a,t,n,s,i,c){e.AlgoliaAnalyticsObject=s,e[s]=e[s]||function(){
(e[s].queue=e[s].queue||[]).push(arguments)},e[s].version=(n.match(/@([^\/]+)\/?/) || [])[1],i=a.createElement(t),c=a.getElementsByTagName(t)[0],
i.async=1,i.src=n,c.parentNode.insertBefore(i,c)
}(window,document,"script",ALGOLIA_INSIGHTS_SRC,"aa");
</script>
If you’re using a package manager, you can upgrade it to the latest version.
1
2
3
npm install search-insights
# or
yarn add search-insights
1
2
3
4
5
6
7
8
9
10
11
12
function App() {
return (
<InstantSearch
// ...
insights={{
insightsClient: window.aa,
}
>
{/* ... */}
</InstantSearch>
);
}
Otherwise, you can remove it and let InstantSearch handle it for you.
Remove search-insights
Starting from v4.55.0, InstantSearch loads search-insights
for you from jsDelivr if not detected in the page. If you’ve installed search-insights
, you can now remove it.
If you’re using the UMD bundle with a <script>
tag, you can remove the snippet:
1
2
3
4
5
6
7
8
- <script>
- var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@2.6.0/dist/search-insights.min.js";
-
- !function(e,a,t,n,s,v,i,c){e.AlgoliaAnalyticsObject=s,e[s]=e[s]||function(){
- (e[s].queue=e[s].queue||[]).push(arguments)},e[s].version=(n.match(/@([^\/]+)\/?/) || [])[1],i=a.createElement(t),c=a.- getElementsByTagName(t)[0],
- i.async=1,i.src=n,c.parentNode.insertBefore(i,c)
- }(window,document,"script",ALGOLIA_INSIGHTS_SRC,"aa");
- </script>
If you’re using a package manager, you can upgrade it to the latest version.
1
2
3
npm uninstall search-insights
# or
yarn remove search-insights
Then you can remove the reference to search-insights
from your code:
1
2
3
4
5
6
7
8
9
10
11
12
function App() {
return (
<InstantSearch
// ...
insights={{
- insightsClient: window.aa,
}
>
{/* ... */}
</InstantSearch>
);
}
InstantSearch loads search-insights
from the jsDelivr CDN, which requires that your site or app allows script execution from foreign resources. Check the security best practices for recommendations.