> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upgrade React InstantSearch

> Upgrade your React InstantSearch version

export const UserToken = () => <Tooltip tip="A user token is a pseudonymous ID that represents an individual user across Algolia searches and events. It links queries, clicks, and conversions to a user profile, enabling user-level analytics, personalization, and recommendations." cta="User token" href=" /doc/guides/sending-events/concepts/usertoken">
    user token
  </Tooltip>;

export const Index = () => <Tooltip tip="An Algolia index is a searchable dataset that consists of records and configuration settings. These settings define how the records are searched and ranked.">
    index
  </Tooltip>;

## Migrate from React InstantSearch v6 to React InstantSearch v7

React InstantSearch v7 is based on [InstantSearch.js](/doc/guides/building-search-ui/what-is-instantsearch/js) and lets you **fully control the rendering**. With React InstantSearch v7, you can create your own UI components using [React Hooks](/doc/api-reference/widgets/react).

<Check>
  This guide helps you migrate from React InstantSearch v6 to React InstantSearch v7.
  If you're using older versions of React InstantSearch, [upgrade to v6 first](https://algolia.com/old-docs/deprecated/instantsearch/react/v6/guides/upgrade-guides/#upgrade-from-v5-to-v6).
</Check>

### Packages

React InstantSearch v7 is available as two packages:

* `react-instantsearch`: it provides Hooks, DOM-based components and server utilities. **This is the recommended package to use if you are building a web app.**
* `react-instantsearch-core`: the core package with all non-DOM APIs such as Hooks, server utilities, and the [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) component. All these APIs are re-exported in `react-instantsearch`. You should use this package if you are building a React Native app, or writing your own UI components.

The following packages from React InstantSearch v6 are no longer available as of React InstantSearch v7:

* `react-instantsearch-dom`: use `react-instantsearch` instead.
* `react-instantsearch-native`: use `react-instantsearch-core` instead.

### Codemods

React InstantSearch provides a [codemod](https://www.codeshiftcommunity.com/docs/) to automatically update your code and use the new package names and APIs from React InstantSearch v7.

What the codemod does:

* Replace `react-instantsearch-dom` imports with `react-instantsearch`.
* Update most prop names and their values.
* Add `TODO` comments for the parts that need manual changes, such as replacing the `defaultRefinement` props, and some `InstantSearch` props.
* Update all translations keys to their new names.
* Replace connectors with their corresponding Hook.

What the codemod doesn't do:

* Replace `react-instantsearch-native` imports. There's no direct equivalent in React InstantSearch v7. You need to update your code manually using `react-instantsearch-core`.
* Update custom components.
* Update types for TypeScript users.

Codemods are a starting point for your migration, to complement with the step-by-step guide below.

You should run the codemod on a separate Git branch, then manually review the changes before deploying them.

Assuming you have a `src/` folder with your React InstantSearch v6 code, you can run the codemod with the following command:

```sh Command line icon=square-terminal theme={"system"}
npx @codeshift/cli -e js,jsx,ts,tsx --packages 'instantsearch-codemods#ris-v6-to-v7' src/
```

Once the command completes, you might find annotations on parts of the code you need to update manually. Search for `TODO (Codemod generated)` in your codebase, which will point you to the right sections of this guide.

Codemods might output code that doesn't follow your coding style. Make sure to run your code formatter before committing the changes.

<CodeGroup>
  ```sh yarn theme={"system"}
  yarn prettier --write 'src/**/*.{js,jsx,ts,tsx}'
  # or
  yarn eslint --fix src/
  ```

  ```sh npm theme={"system"}
  npm exec prettier --write 'src/**/*.{js,jsx,ts,tsx}'
  # or
  npm exec eslint --fix src/
  ```
</CodeGroup>

### Components

React InstantSearch v7 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 v6              | React InstantSearch v7                                                                                                                     |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `Breadcrumb`                        | [`Breadcrumb`](/doc/api-reference/widgets/breadcrumb/react) <br /> Prop changes (see [step-by-step](#breadcrumb))                          |
| `ClearRefinements`                  | [`ClearRefinements`](/doc/api-reference/widgets/clear-refinements/react) <br /> Prop changes (see [step-by-step](#clearrefinements))       |
| `Configure`                         | [`Configure`](/doc/api-reference/widgets/configure/react) <br /> No changes                                                                |
| `CurrentRefinements`                | [`CurrentRefinements`](/doc/api-reference/widgets/current-refinements/react) <br /> Prop changes (see [step-by-step](#currentrefinements)) |
| `DynamicWidgets`                    | [`DynamicWidgets`](/doc/api-reference/widgets/dynamic-facets/react) <br /> No changes                                                      |
| `ExperimentalConfigureRelatedItems` | [`RelatedProducts`](/doc/api-reference/widgets/related-products/react) <br /> No UI component                                              |
| `HierarchicalMenu`                  | [`HierarchicalMenu`](/doc/api-reference/widgets/hierarchical-menu/react) <br /> Prop changes (see [step-by-step](#hierarchicalmenu))       |
| `Highlight`                         | [`Highlight`](/doc/api-reference/widgets/highlight/react) <br /> Prop changes (see [step-by-step](#highlight))                             |
| `Hits`                              | [`Hits`](/doc/api-reference/widgets/hits/react) <br /> No changes                                                                          |
| `HitsPerPage`                       | [`HitsPerPage`](/doc/api-reference/widgets/hits-per-page/react) <br /> Prop changes (see [step-by-step](#hitsperpage))                     |
| `Index`                             | [`Index`](/doc/api-reference/widgets/index-widget/react) <br /> No changes                                                                 |
| `InfiniteHits`                      | [`InfiniteHits`](/doc/api-reference/widgets/infinite-hits/react) <br /> Prop changes (see [step-by-step](#infinitehits))                   |
| `InstantSearch`                     | [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) <br /> Prop changes (see [step-by-step](#instantsearch))                 |
| `Menu`                              | [`Menu`](/doc/api-reference/widgets/menu/react) <br /> Prop changes (see [step-by-step](#menu))                                            |
| `MenuSelect`                        | [`useMenu`](/doc/api-reference/widgets/menu/react#hook) <br /> No UI component                                                             |
| `NumericMenu`                       | [`useConnector(connectNumericMenu)`](/doc/api-reference/widgets/connector/react) <br /> No UI component                                    |
| `Pagination`                        | [`Pagination`](/doc/api-reference/widgets/pagination/react) <br /> Prop changes (see [step-by-step](#pagination))                          |
| `Panel`                             | No equivalent available                                                                                                                    |
| `PoweredBy`                         | [`PoweredBy`](/doc/api-reference/widgets/powered-by/react) <br /> Prop changes (see [step-by-step](#poweredby))                            |
| `QueryRuleContext`                  | [`useQueryRules`](/doc/api-reference/widgets/query-rules/react) <br /> No UI component                                                     |
| `QueryRuleCustomData`               | [`useQueryRules`](/doc/api-reference/widgets/query-rules/react) <br /> No UI component                                                     |
| `RangeInput`                        | [`RangeInput`](/doc/api-reference/widgets/range-input/react) <br /> Prop changes (see [step-by-step](#rangeinput))                         |
| `RangeSlider`                       | [`useRange`](/doc/api-reference/widgets/range-input/react#hook) <br /> No UI component                                                     |
| `RatingMenu`                        | [`useMenu`](/doc/api-reference/widgets/menu/react#hook) <br /> No UI component                                                             |
| `RefinementList`                    | [`RefinementList`](/doc/api-reference/widgets/refinement-list/react) <br /> Prop changes (see [step-by-step](#refinementlist))             |
| `RelevantSort`                      | [`useConnector(connectRelevantSort)`](/doc/api-reference/widgets/connector/react) <br /> No UI component                                   |
| `ScrollTo`                          | No equivalent available ([read more](#scrollto))                                                                                           |
| `SearchBox`                         | [`SearchBox`](/doc/api-reference/widgets/search-box/react) <br /> Prop changes (see [step-by-step](#searchbox))                            |
| `Snippet`                           | [`Snippet`](/doc/api-reference/widgets/snippet/react) <br /> Prop changes (see [step-by-step](#snippet))                                   |
| `SortBy`                            | [`SortBy`](/doc/api-reference/widgets/sort-by/react) <br /> Prop changes (see [step-by-step](#sortby))                                     |
| `Stats`                             | [`Stats`](/doc/api-reference/widgets/stats/react) <br /> Prop changes (see [step-by-step](#stats))                                         |
| `ToggleRefinement`                  | [`ToggleRefinement`](/doc/api-reference/widgets/toggle-refinement/react) <br /> Prop changes (see [step-by-step](#togglerefinement))       |
| `VoiceSearch`                       | [`useConnector(connectVoiceSearch)`](/doc/api-reference/widgets/connector/react) <br /> No UI component                                    |

#### `Breadcrumb`

##### Replace `rootURL` with `rootPath`

The [`Breadcrumb`](/doc/api-reference/widgets/breadcrumb/react) component now takes an optional `rootPath` prop which replaces `rootURL`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Breadcrumb
-     rootURL="/audio"
+     rootPath="Audio"
    />
  );
}
```

##### Replace `translations`

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Breadcrumb
      translations={{
-       rootLabel: 'Home',
+       rootElementText: 'Home',
      }}
    />
  );
}
```

#### `ClearRefinements`

##### Replace `clearsQuery` with `includedAttributes` or `excludedAttributes`

The [`ClearRefinements`](/doc/api-reference/widgets/clear-refinements/react) component now takes optional `includedAttributes` and `excludedAttributes` prop which replace `clearsQuery`.

By default, the component ignores the [`query`](/doc/api-reference/api-parameters/query) parameter.

If `clearsQuery` is set to `false` in your app (or not set at all), you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <ClearRefinements
-     clearsQuery={false}
    />
  );
}
```

If `clearsQuery` is set to `true` in your app, you can remove [`query`](/doc/api-reference/api-parameters/query) from `excludedAttributes`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <ClearRefinements
-     clearsQuery
+     excludedAttributes={[]}
    />
  );
}
```

Alternatively, you can add [`query`](/doc/api-reference/api-parameters/query) to `includedAttributes` if you're already hand-picking what attributes to clear. Keep in mind that when you specify `includedAttributes`, only those attributes get cleared.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <ClearRefinements
-     clearsQuery={false}
+     includedAttributes={['query']}
    />
  );
}
```

<Note>
  The `includedAttributes` and `excludedAttributes` props can't be used together.
</Note>

##### Replace `translations`

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <ClearRefinements
      translations={{
-       reset: 'Clear all filters',
+       resetButtonText: 'Clear all',
      }}
    />
  );
}
```

#### `CurrentRefinements`

##### Replace `clearsQuery `with `includedAttributes` or `excludedAttributes`

The [`CurrentRefinements`](/doc/api-reference/widgets/current-refinements/react) component now takes optional `includedAttributes` and `excludedAttributes` prop which replace `clearsQuery`.

By default, the component ignores the [`query`](/doc/api-reference/api-parameters/query) parameter. If `clearsQuery` is set to `true` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <CurrentRefinements
-     clearsQuery
    />
  );
}
```

If `clearsQuery` is set to `false` in your app (or not set at all), you can remove [`query`](/doc/api-reference/api-parameters/query) from `excludedAttributes`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <CurrentRefinements
-     clearsQuery={false}
+     excludedAttributes={[]}
    />
  );
}
```

Alternatively, you can add [`query`](/doc/api-reference/api-parameters/query) to `includedAttributes` if you're already hand-picking what attributes to clear. Keep in mind that when you specify `includedAttributes`, only those attributes get cleared.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <CurrentRefinements
-     clearsQuery={false}
+     includedAttributes={['query']}
    />
  );
}
```

<Note>
  The `includedAttributes` and `excludedAttributes` props can't be used together.
</Note>

#### `HierarchicalMenu`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`HierarchicalMenu`](/doc/api-reference/widgets/hierarchical-menu/react) component no longer accepts a `defaultRefinement` prop.
Instead, specify an initial UI state on your `InstantSearch` component.
Replace `YourIndexName` with the name of your Algolia <Index />.

```diff React icon=code theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       YourIndexName: {
+         hierarchicalMenu: {
+           'hierarchicalCategories.lvl0': [
+             'Audio > Home Audio',
+           ],
+         },
+       },
+     }}
    >
      <HierarchicalMenu
-       defaultRefinement="Audio > Home Audio"
      />
    </InstantSearch>
  );
}
```

##### Replace `facetOrdering` with `sortBy`

By default, the component sorts categories with the `sortBy` prop using the rules of [`renderingContent.facetOrdering`](/doc/guides/building-search-ui/ui-and-ux-patterns/facet-display/react) when set, and falls back on ascending name (`"name:asc"`).

If `facetOrdering` is set to `true` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <HierarchicalMenu
-     facetOrdering
    />
  );
}
```

If `facetOrdering` is set to `false` in your app,
you can set the `sortBy` prop with the sorting annotation of your choice,
or using a custom sorting function.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <HierarchicalMenu
+     sortBy={['name:asc']}
+     // or
+     sortBy={(a, b) => {
+       return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
+     }}
    />
  );
}
```

##### Replace translations

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <HierarchicalMenu
      translations={{
-       showMore(expanded) {
-         return expanded ? 'Show less' : 'Show more';
-       },
+       showMoreButtonText({ isShowingMore }) {
+         return isShowingMore ? 'Show less' : 'Show more';
+       },
      }}
    />
  );
}
```

#### `Highlight`

##### Replace `tagName` with `highlightedTagName`

The [`Highlight`](/doc/api-reference/widgets/highlight/react) component now takes an optional `highlightedTagName` prop which replaces `tagName`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Highlight
-     tagName="span"
+     highlightedTagName="span"
    />
  );
}
```

The default value also changed from `"em"` to `"mark"` for `highlightedTagName` (formerly `tagName`). If `tagName` is to `"mark"` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Highlight
-     tagName="mark"
    />
  );
}
```

Conversely, if you were relying on the default value for `tagName`, you should now set it explicitly.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Highlight
+     highlightedTagName="em"
    />
  );
}
```

#### `HitsPerPage`

##### Replace `defaultRefinement` with `default` in items

The [`HitsPerPage`](/doc/api-reference/widgets/hits-per-page/react) component no longer takes a required `defaultRefinement` prop. Instead, you can specify which item is selected by default by specifying a boolean `default` property in the `items` prop.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <HitsPerPage
-     defaultRefinement={5}
      items={[
        {
          label: '5 hits per page',
          value: 5,
+         default: true,
        },
        {
          label: '10 hits per page',
          value: 10,
        },
      ]}
    />
  );
}
```

#### `InfiniteHits`

##### Import `createInfiniteHitsSessionStorageCache` from Instantsearch.js

The built-in [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) implementation for the `cache` prop are now available from the `instantsearch.js` package.

```diff React icon=code theme={"system"}
-import { createInfiniteHitsSessionStorageCache } from 'react-instantsearch-dom';
+import { createInfiniteHitsSessionStorageCache } from 'instantsearch.js/es/lib/infiniteHitsCache';
```

##### Replace translations

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <InfiniteHits
      translations={{
-       loadPrevious: 'Load previous',
+       showPreviousButtonText: 'Load previous',
-       loadMore: 'Load more',
+       showMoreButtonText: 'Load more',
      }}
    />
  );
}
```

#### `InstantSearch`

##### Replace `searchState` with `initialUiState`

The [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) component now takes optional `initialUiState` prop which replaces `searchState`.

Replace `YourIndexName` with the name of your Algolia index.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <InstantSearch
-     searchState={{
-       query: 'iphone',
-       hitsPerPage: 5,
-     }}
+     initialUiState={{
+       YourIndexName: {
+         query: 'iphone',
+         hitsPerPage: 5,
+       },
+     }}
    >
      {/* … */}
    </InstantSearch>
  );
}
```

<Note>
  To provide an initial [`uiState`](/doc/api-reference/widgets/ui-state/react), you must add the corresponding widgets to your implementation. In the previous example, you need to mount a [`SearchBox`](/doc/api-reference/widgets/search-box/react) component and a [`HitsPerPage`](/doc/api-reference/widgets/hits-per-page/react) component (either built-in or [virtual](/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react#build-virtual-widgets-with-hooks)) for applying state to queries.
</Note>

You must nest the state passed to `initialUiState` under the index name it applies to, even if your implementation targets a single index. If you're doing [multi-index](/doc/guides/building-search-ui/ui-and-ux-patterns/multi-index-search/react), each piece of state must be nested under its own index. To share state between indices, you must repeat it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <InstantSearch
-     searchState={{
-       query: 'phone',
-       indices: {
-         index1: {
-           refinementList: {
-             brand: ['Apple'],
-           },
-         },
-         index2: {
-           refinementList: {
-             brand: ['Samsung'],
-           },
-         },
-       }
-     }}
+     initialUiState={{
+       index1: {
+         query: 'phone',
+         refinementList: {
+           brand: ['Apple'],
+         },
+       },
+       index2: {
+         query: 'phone',
+         refinementList: {
+           brand: ['Samsung'],
+         },
+       },
+     }}
    >
      {/* … */}
    </InstantSearch>
  );
}
```

##### Replace `onSearchStateChange` with `onStateChange`

If you were using `onSearchStateChange` to control the instance and react to state changes, you can replace it with `onStateChange`. Using this prop makes you responsible for updating the state, using the exposed [`uiState`](/doc/api-reference/widgets/ui-state/react) and its setter `setUiState`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <InstantSearch
-     onSearchStateChange={(searchState) => {
-       // Custom logic
-     }}
+     onStateChange={({ uiState, setUiState }) => {
+       // Custom logic
+       setUiState(uiState);
+     }}
    >
      {/* … */}
    </InstantSearch>
  );
}
```

##### Replace refresh prop with refresh from useInstantSearch()

The `refresh` prop was removed. If you need to manually update the cache and refresh the frontend, you can replace the `refresh` with an imperative call to `refresh` from the `useInstantSearch()` Hook.

```diff React icon=code theme={"system"}
function Search() {
+ const [shouldRefresh] = useState(false);
+ const { refresh } = useInstantSearch();

+ useEffect(() => {
+   refresh();
+ }, [shouldRefresh]);

  return <>{/* Your JSX */}</>;
}

function App() {
  return (
    <InstantSearch
-     refresh
    >
      <Search />
    </InstantSearch>
  );
}
```

##### Replace `resultsState`, `findResultsState`, and `onSearchParameters` with server-side rendering APIs

The server APIs have been simplified. If you were using `resultsState`, `findResultsState` and `onSearchParameters` for server-side rendering, check the [server-side rendering](#server-side-rendering-ssr) section to migrate.

<Note>
  If you were using `onSearchParameters` for another use case than server-side rendering, please [reach out](https://github.com/algolia/instantsearch/discussions/new?category=q-a\&labels=triage).
</Note>

#### Move createURL in routing

If you were using `createURL` to manipulate the URL, you can move it in the `routing` prop.

```diff React icon=code theme={"system"}
+import { history } from 'instantsearch.js/es/lib/routers';

 function Search() {
   return (
     <InstantSearch
-      createURL={(searchState) => `?q=${searchState.query}`}
       routing={{
         router: history({
           // …
           createURL({ qsModule, routeState, location }) {
             return `?q=${routeState.query}`;
           }
         }),
       }}
     >
       {/* … */}
     </InstantSearch>
   );
 }
```

For more in-depth guidance and examples with various routing methods, check the [routing guide](/doc/guides/building-search-ui/going-further/routing-urls/react).

#### `Menu`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`Menu`](/doc/api-reference/widgets/menu/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         menu: {
+           'categories': [
+             'Apple',
+           ],
+         },
+       },
+     }}
    >
      <Menu
        attribute="categories"
-       defaultRefinement="Apple"
      />
    </InstantSearch>
  );
}
```

##### Replace `facetOrdering `with `sortBy`

By default, the component sorts categories with the `sortBy` prop using the rules of [`renderingContent.facetOrdering`](/doc/guides/building-search-ui/ui-and-ux-patterns/facet-display/react) when set, and falls back on refined item and ascending name (`["isRefined", "name:asc"]`).

If `facetOrdering` is set to `true` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Menu
-     facetOrdering
    />
  );
}
```

If `facetOrdering` is set to `false` in your app, you can set the `sortBy` prop with the sorting annotation of your choice, or using a custom sorting function.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Menu
+     sortBy={['isRefined', 'name:asc']}
+     // or
+     sortBy={(a, b) => {
+       if (a.isRefined && b.isRefined) {
+         return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
+       }
+
+       return a.isRefined ? -1 : 1;
+     }}
    />
  );
}
```

##### Replace searchable with a custom implementation

The [`Menu`](/doc/api-reference/widgets/menu/react) widget isn't searchable. You can create a searchable version by using a custom widget with the [`useRefinementList()`](/doc/api-reference/widgets/refinement-list/react#hook) connector.

```jsx React icon=code theme={"system"}
import {
  useRefinementList,
  useInstantSearch,
} from 'react-instantsearch';

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: '',
  };
}
```

##### Replace translations

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <HierarchicalMenu
      translations={{
-       showMore(expanded) {
-         return expanded ? 'Show less' : 'Show more';
-       },
+       showMoreButtonText({ isShowingMore }) {
+         return isShowingMore ? 'Show less' : 'Show more';
+       },
      }}
    />
  );
}
```

#### `Pagination`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`Pagination`](/doc/api-reference/widgets/pagination/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         page: 2,
+       },
+     }}
    >
      <Pagination
-       defaultRefinement={2}
      />
    </InstantSearch>
  );
}
```

##### Replace translations

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Pagination
      translations={{
-       previous: 'Previous page',
+       previousPageItemText: 'Previous page',
-       next: 'Next page',
+       nextPageItemText: 'Next page',
-       first: 'First page',
+       firstPageItemText: 'First page',
-       last: 'Last page',
+       lastPageItemText: 'Last page',
-       page: (currentRefinement) => `Page ${currentRefinement}`,
+       pageItemText: ({ currentPage }) => `Page ${currentPage}`,
-       ariaPrevious: 'Previous page',
+       previousPageItemAriaLabel: 'Previous page',
-       ariaNext: 'Next page',
+       nextPageItemAriaLabel: 'Next page',
-       ariaFirst: 'First page',
+       firstPageItemAriaLabel: 'First page',
-       ariaLast: 'Last page',
+       lastPageItemAriaLabel: 'Last page',
-       ariaPage: (currentRefinement) => `Go to page ${currentRefinement}`,
+       pageItemAriaLabel: ({ currentPage }) => `Go to page ${currentPage}`,
      }}
    />
  );
}
```

#### `PoweredBy`

##### Replace translations with a custom implementation

The Algolia logo is now a single image, including the "Search by", therefore the `translations` prop was removed. If you need to translate the image, you can use a custom widget with the [`usePoweredBy()`](/doc/api-reference/widgets/powered-by/react#hook) connector.

```jsx React icon=code theme={"system"}
import { usePoweredBy } from "react-instantsearch";

function CustomPoweredBy() {
  const { url } = usePoweredBy();

  // Download and customize the "Search by Algolia" logo for light and dark themes.
  // https://algolia.frontify.com/d/1AZwVNcFZiu7/style-guide#/basics/algolia-logo

  return <>{/* Your JSX */}</>;
}
```

#### `RangeInput`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`RangeInput`](/doc/api-reference/widgets/range-input/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         range: { min: 10, max: 500 },
+       },
+     }}
    >
      <RangeInput
-       defaultRefinement={{ min: 10, max: 500 }}
      />
    </InstantSearch>
  );
}
```

##### Replace translations

The `translations` keys have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <RangeInput
      translations={{
-       submit: 'Apply',
+       submitButtonText: 'Apply',
-       separator: '-',
+       separatorElementText: '-',
      }}
    />
  );
}
```

#### `RefinementList`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`RefinementList`](/doc/api-reference/widgets/refinement-list/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         refinementList: {
+           brand: ['Apple'],
+         },
+       },
+     }}
    >
      <RefinementList
        attribute="brand"
-       defaultRefinement={['Apple']}
      />
    </InstantSearch>
  );
}
```

##### Replace `facetOrdering` with `sortBy`

By default, the component sorts categories with the `sortBy` prop using the rules of [`renderingContent.facetOrdering`](/doc/guides/building-search-ui/ui-and-ux-patterns/facet-display/react) when set, and falls back on refined item, descending count, and ascending name (`["isRefined", "count:desc", "name:asc"]`).

If `facetOrdering` is set to `true` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <RefinementList
-     facetOrdering
    />
  );
}
```

If `facetOrdering` is set to `false` in your app, you can set the `sortBy` prop with the sorting annotation of your choice, or using a custom sorting function.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <RefinementList
+     sortBy={['isRefined', 'count:desc', 'name:asc']}
+     // or
+     sortBy={(a, b) => {
+       if (a.isRefined && b.isRefined) {
+         if (a.count === b.count) {
+           return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
+         }
+
+         return a.count > b.count ? -1 : 1;
+       }
+
+       return a.isRefined ? -1 : 1;
+     }}
    />
  );
}
```

##### Replace translations

The `translations` keys have changed. Additionally, the `placeholder` translation now has its own top-level prop, `searchablePlaceholder`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <RefinementList
      translations={{
-       showMore(expanded) {
-         return expanded ? 'Show less' : 'Show more';
-       },
+       showMoreButtonText({ isShowingMore }) {
+         return isShowingMore ? 'Show less' : 'Show more';
+       },
-       noResults: 'No results.',
+       noResultsText: 'No results.',
-       submitTitle: 'Submit',
+       submitButtonTitle: 'Submit',
-       resetTitle: 'Reset',
+       resetButtonTitle: 'Reset',
-       placeholder: 'Search',
      }}
+     searchablePlaceholder="Search"
    />
  );
}
```

#### `ScrollTo`

The component provided a way to scroll to the top of search results each time the [`uiState`](/doc/api-reference/widgets/ui-state/react) changed (due to a page change for example).
This can be done in React InstantSearch v7 by using the [`useEffect`](https://react.dev/reference/react/useEffect) React Hook or by adding an InstantSearch middleware through [`useInstantSearch()`](/doc/api-reference/widgets/use-instantsearch/react), in combination with [`scrollIntoView`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView).
You can see an implementation [in the ecommerce example](https://github.com/algolia/instantsearch/blob/master/examples/react/e-commerce/components/ScrollTo.tsx).

#### `SearchBox`

##### Replace` defaultRefinement` with `initialUiState` on `InstantSearch`

The [`RefinementList`](/doc/api-reference/widgets/refinement-list/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         query: 'iphone',
+       },
+     }}
    >
      <SearchBox
-       defaultRefinement="iphone"
      />
    </InstantSearch>
  );
}
```

##### Replace showLoadingIndicator with custom CSS

The widget now always shows a loading indicator when the search is stalled. If `showLoadingIndicator` is set to `false` in your app (or not set at all), you can hide the loading indicator with CSS.

```css CSS theme={"system"}
.ais-SearchBox-loadingIndicator {
  display: none;
}
```

##### Replace `submit` with `submitIconComponent`, `reset` with `resetIconComponent`, and `loadingIndicator` with `loadingIconComponent`

To customize the submit, reset, and loading icons, you can use the `submitIconComponent`, `resetIconComponent`, and `loadingIconComponent` props. Unlike `submit`, `reset`, and `loadingIndicator`, these new props take React components. They give you access to the default and passed `classNames`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <SearchBox
-     submit={<div>Submit</div>}
+     submitIconComponent={({ classNames }) => (
+       <div className={classNames.submitIcon}>Submit</div>
+     )}
-     reset={<div>Reset</div>}
+     resetIconComponent={({ classNames }) => (
+       <div className={classNames.resetIcon}>Reset</div>
+     )}
-     loadingIndicator={<div>Loading</div>}
+     loadingIconComponent={({ classNames }) => (
+       <div className={classNames.loadingIcon}>Loading</div>
+     )}
    />
  );
}
```

##### Replace `focusShortcuts` with custom code

The `focusShortcuts` prop was removed. If you want to focus the search box with custom keyboard shortcuts, you can set it up yourself with custom code.

For example, you can use [`react-use`](https://github.com/streamich/react-use) with the [`useKey`](https://github.com/streamich/react-use/blob/master/docs/useKey.md) Hook.

```diff React icon=code theme={"system"}
+import { useKey } from 'react-use';

 function Search() {
+  useKey('s', (event) => {
+    event.preventDefault();
+    document.querySelector('.ais-SearchBox-input').focus();
+  });

   return (
     <SearchBox
-      focusShortcuts={['s']}
     />
   );
 }
```

##### Replace translations.placeholder with placeholder

The `placeholder` translation now has its own top-level prop, `placeholder`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <SearchBox
-     translations={{
-       placeholder: 'Search here',
-     }}
+     placeholder="Search here"
    />
  );
}
```

#### `Snippet`

##### Replace `tagName` with `highlightedTagName`

The [`Highlight`](/doc/api-reference/widgets/highlight/react) component now takes an optional `highlightedTagName` prop which replaces `tagName`.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Snippet
-     tagName="span"
+     highlightedTagName="span"
    />
  );
}
```

The default value also changed from `"em"` to `"mark"` for `highlightedTagName` (formerly `tagName`). If `tagName` is to `"mark"` in your app, you can remove it.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Snippet
-     tagName="mark"
    />
  );
}
```

Conversely, if you were relying on the default value for `tagName`, you should now set it explicitly.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Snippet
+     highlightedTagName="em"
    />
  );
}
```

#### `SortBy`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`SortBy`](/doc/api-reference/widgets/sort-by/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         sortBy: 'instant_search',
+       },
+     }}
    >
      <SortBy
-       defaultRefinement="instant_search"
      />
    </InstantSearch>
  );
}
```

#### `Stats`

##### Replace translations

The `translations` key have changed.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <RefinementList
      translations={{
-       stats(nbHits, processingTimeMS, nbSortedHits, areHitsSorted) {
+       rootElementText({ nbHits, processingTimeMS, nbSortedHits, areHitsSorted }) {
          return areHitsSorted && nbHits !== nbSortedHits
          ? `${nbSortedHits!.toLocaleString()} relevant results sorted out of ${nbHits.toLocaleString()} found in ${processingTimeMS.toLocaleString()}ms`
          : `${nbHits.toLocaleString()} results found in ${processingTimeMS.toLocaleString()}ms`
        },
      }}
    />
  );
}
```

#### `ToggleRefinement`

##### Replace `defaultRefinement` with `initialUiState` on `InstantSearch`

The [`ToggleRefinement`](/doc/api-reference/widgets/toggle-refinement/react) component no longer accepts a `defaultRefinement` prop. Instead, you can specify an initial UI state on your `InstantSearch` component.

```diff JavaScript theme={"system"}
function App() {
  return (
    <InstantSearch
+     initialUiState={{
+       <<Your-Index-Name-Here>>: {
+         toggle: {
+           free_shipping: true
+         },
+       },
+     }}
    >
      <ToggleRefinement
        attribute="free_shipping"
-       defaultRefinement={true}
      />
    </InstantSearch>
  );
}
```

##### Replace value with on and off

By default, [`ToggleRefinement`](/doc/api-reference/widgets/toggle-refinement/react) uses respectively `true` and `undefined` for new optional props `on` and `off`. You can replace the `value` prop with `on` (and `off`, if needed).

```diff React icon=code theme={"system"}
function Search() {
  return (
    <ToggleRefinement
-     value={'yes'}
+     on={'yes'}
    />
  );
}
```

### Connectors

React InstantSearch v7 provides the same connectors as React InstantSearch v6 except the `connectStateResults` connector, which isn't available in React InstantSearch v7.

If you were using connectors with higher-order components (HOCs), you can migrate to Hooks.

```diff JavaScript theme={"system"}
-import { connectSearchBox } from 'react-instantsearch-dom';
+import { useSearchBox } from 'react-instantsearch';

 function SearchBox({
- currentRefinement,
+ query,
  isSearchStalled,
  refine
 }) {
   return (
     <form noValidate action="" role="search">
       <input
         type="search"
-        value={currentRefinement}
+        value={query}
         onChange={(event) => refine(event.currentTarget.value)}
       />
       <button onClick={() => refine('')}>Reset query</button>
       {isSearchStalled ? 'My search is stalled' : ''}
     </form>
   );
 }

-const CustomSearchBox = connectSearchBox(SearchBox);

+function CustomSearchBox(props) {
+ const searchBoxApi = useSearchBox(props);

+ return <SearchBox {...searchBoxApi} />;
+}
```

#### `connectStateResults`

React InstantSearch v7 doesn't have the `connectStateResults` connector.
You can use the [`useInstantSearch()`](/doc/api-reference/widgets/use-instantsearch/react) hook instead.

### Connectors and Hooks

React InstantSearch v7 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.

The React InstantSearch v7 package uses TypeScript natively.
If your editor supports code completion (IntelliSense), you can use it to discover the new props.

### Higher-Order Components (HOCs)

React InstantSearch v7 doesn't come with Higher Order Components (HOCs).
However, you can create them by wrapping existing Hooks into simple HOCs:

```jsx React icon=code theme={"system"}
import { useSearchBox } from "react-instantsearch";

const connectSearchBox = (Component) => {
  const SearchBox = (props) => {
    const data = useSearchBox();

    return <Component {...props} {...data} />;
  };

  return SearchBox;
};

function RawSearchBox(props) {
  const { refine, query } = props;

  return (
    <form type="search">
      <input
        type="search"
        value={query}
        onChange={(event) => refine(event.currentTarget.value)}
      />
    </form>
  );
}

const SearchBox = connectSearchBox(RawSearchBox);
```

You can also combine that with the [`useConnector()`](/doc/api-reference/widgets/connector/react) Hook to create a connector.

```jsx React icon=code theme={"system"}
import { useConnector } from "react-instantsearch";

const connectCustom = (Component) => {
  const Custom = (props) => {
    // myConnector.connectCustom is an InstantSearch.js connector
    const data = useConnector(myConnector.connectCustom, props);

    return <Component {...props} {...data} />;
  };

  return Custom;
};

function RawCustom(props) {
  const { refine, currentRefinement } = props;

  return (
    <button onClick={() => refine(currentRefinement)}>
      {currentRefinement}
    </button>
  );
}

const Custom = connectCustom(RawCustom);
```

### Creating connectors

The connector API has changed to use InstantSearch.js connectors.
**The previous `createConnector()` function is no longer available.**

React InstantSearch v7 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](/doc/guides/building-search-ui/widgets/create-your-own-widgets/react#build-a-custom-connector).

For more information, see [Custom Hooks](/doc/guides/building-search-ui/what-is-instantsearch/react#custom-hooks).

### Routing

Routing now follows the InstantSearch.js routing APIs with the `InstantSearch` [routing](/doc/api-reference/widgets/instantsearch/react#param-routing) prop.

For more information, see:

* [InstantSearch routing prop](/doc/api-reference/widgets/instantsearch/react#param-routing)
* [Sync Your URLs with InstantSearch.js](/doc/guides/building-search-ui/going-further/routing-urls/js)

### Server-side rendering (SSR)

The server APIs have been simplified in React InstantSearch v7.

Replace `findResultsState()` with [`getServerState()`](/doc/api-reference/widgets/server-state/react).
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`.

```diff server.js icon=code theme={"system"}
 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';
 import App from './App';

-const indexName = 'instant_search';
-const searchClient = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_SEARCH_API_KEY');

 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`](/doc/api-reference/widgets/ssr-provider/react).
Then, pass the server state by spreading the [`getServerState()`](/doc/api-reference/widgets/server-state/react) prop.

```diff App.js icon=code theme={"system"}
 import { liteClient as algoliasearch } from 'algoliasearch/lite';
 import React from 'react';
-import { InstantSearch } from 'react-instantsearch-dom';
+import {
+  InstantSearch,
+  InstantSearchSSRProvider,
+} from 'react-instantsearch';

 const searchClient = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_SEARCH_API_KEY');

 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;
```

For more information, see:

* **[`getServerState`](/doc/api-reference/widgets/server-state/react)**
* **[`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react)**
* **[Server-side rendering with React InstantSearch](/doc/guides/building-search-ui/going-further/server-side-rendering/react)**

### Types

Types are now available in React InstantSearch v7.

You can uninstall all InstantSearch types coming from [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped).

```sh Command line icon=square-terminal theme={"system"}
npm uninstall @types/react-instantsearch-dom @types/react-instantsearch-core @types/react-instantsearch
```

## Migrate from React InstantSearch Hooks to React InstantSearch v7

From v7, React InstantSearch Hooks is called React InstantSearch.

The migration is straightforward as the APIs are the same. Only package names have changed, and several APIs have been deprecated.

### Package names

| Before                                    | After                               |
| ----------------------------------------- | ----------------------------------- |
| `react-instantsearch-hooks`               | `react-instantsearch-core`          |
| `react-instantsearch-hooks-web`           | `react-instantsearch`               |
| `react-instantsearch-hooks-server`        | `react-instantsearch`               |
| `react-instantsearch-hooks-router-nextjs` | `react-instantsearch-router-nextjs` |

Update your `package.json` file:

```diff package.json icon=braces theme={"system"}
{
  "dependencies": {
-   "react-instantsearch-hooks": "^6.42.0",
+   "react-instantsearch-core": "^7.0.0",
-   "react-instantsearch-hooks-web": "^6.42.0",
+   "react-instantsearch": "^7.0.0",
-   "react-instantsearch-hooks-server": "^6.42.0",
-   "react-instantsearch-hooks-router-nextjs": "^6.42.0",
+   "react-instantsearch-router-nextjs": "^7.0.0",
  }
}
```

After updating your `package.json` file, run `npm install` to install the updated dependencies.

You can then change your imports:

```diff JavaScript icon=code theme={"system"}
-import { useInstantSearch } from 'react-instantsearch-hooks';
+import { useInstantSearch } from 'react-instantsearch-core';
-import { SearchBox } from 'react-instantsearch-hooks-web';
-import { getServerState } from 'react-instantsearch-hooks-server';
+import { SearchBox, getServerState } from 'react-instantsearch';
-import { createInstantSearchRouterNext } from 'react-instantsearch-hooks-router-nextjs';
+import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';
```

<Info>
  The `react-instantsearch-hooks-server` functions are now included in `react-instantsearch`.
</Info>

### Codemods

To automatically update your code to use the new package names and APIs from React InstantSearch v7, Algolia provides a [codemod](https://www.codeshiftcommunity.com/docs/).

First, uninstall the `react-instantsearch-hooks-*` packages and install their `react-instantsearch-*` counterparts.

```sh Command line icon=square-terminal theme={"system"}
npm uninstall react-instantsearch-hooks-web react-instantsearch-hooks react-instantsearch-hooks-server
npm install react-instantsearch react-instantsearch-core
```

Assuming your source code is in the `src/` folder, run the following command:

```sh Command line icon=square-terminal theme={"system"}
npx @codeshift/cli -e js,jsx,ts,tsx --packages 'instantsearch-codemods#rish-to-ris' src/
```

Afterwards you should run your code formatter as codemods might output code that doesn't follow your code style:

<CodeGroup>
  ```sh yarn theme={"system"}
  yarn prettier --write 'src/**/*.{js,jsx,ts,tsx}'
  # or
  yarn eslint --fix src/
  ```

  ```sh npm theme={"system"}
  npm exec prettier --write 'src/**/*.{js,jsx,ts,tsx}'
  # or
  npm exec eslint --fix src/
  ```
</CodeGroup>

### `use` to `addMiddlewares`

[`useInstantSearch`](/doc/api-reference/widgets/use-instantsearch/react)'s `use` function has been renamed to `addMiddlewares`.

```diff JavaScript icon=code theme={"system"}
import { useInstantSearch } from 'react-instantsearch-core';

function Middleware() {
- const { use } = useInstantSearch();
+ const { addMiddlewares } = useInstantSearch();

  useEffect(() => {
    const middleware = /* ... */;

-   return use(middleware);
+   return addMiddlewares(middleware);
- }, [use]);
+ }, [addMiddlewares]);

  return null;
}
```

### `getServerState`

The [`getServerState`](/doc/api-reference/widgets/server-state/react) function now requires `renderToString` from `react-dom/server` to be passed as a second argument.

```diff JavaScript icon=code theme={"system"}
+import { renderToString } from 'react-dom/server';
import { getServerState } from 'react-instantsearch';

function App() {
  return (
    /* ... */
  );
}

async function getServerSideProps() {
- const serverState = await getServerState(<App />);
+ const serverState = await getServerState(<App />, { renderToString });

  return {
    props: {
      serverState,
    },
  };
}
```

### `Stats`

The `translations` prop of the [`Stats`](/doc/api-reference/widgets/stats/react) widget has changed and now accepts a `rootElementText` function instead of a `stats` function.

```diff React icon=code theme={"system"}
function Search() {
  return (
    <Stats
      translations={{
-       stats({ nbHits, processingTimeMS }) {
+       rootElementText({ nbHits, processingTimeMS }) {
          return `${nbHits.toLocaleString()} results found in ${processingTimeMS.toLocaleString()}ms`;
        },
      }}
    />
  );
}
```

## Upgrade event tracking

**Starting from v6.43.0, React InstantSearch Hooks simplifies the event tracking process with the [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights) option.**
You no longer need to install the [`search-insights`](/doc/libraries/search-insights) library or set up the [`insights`](/doc/api-reference/widgets/insights/react) middleware yourself.

Here are some benefits when using the [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights) option:

* It better handles the <UserToken />. Once you set it, all search and event tracking calls include the token.
* It automatically sends default events from built-in widgets such as [`RefinementList`](/doc/api-reference/widgets/refinement-list/react), [`Menu`](/doc/api-reference/widgets/menu/react), 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`](/doc/libraries/search-insights) or with the [`insights`](/doc/api-reference/widgets/insights/react) middleware, you should:

1. Upgrade `react-instantsearch-hooks` to v6.43.0 or greater
2. [Migrate](#use-the-insights-option) from using the [`insights`](/doc/api-reference/widgets/insights/react) middleware to the [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights) option
3. Either [update](#update-search-insights) or [remove](#remove-search-insights) the [`search-insights`](/doc/libraries/search-insights) library

### Use the `insights` option

**Starting from v6.43.0, InstantSearch lets you enable event tracking with the [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights) option.** You no longer need to set up the [`insights`](/doc/api-reference/widgets/insights/react) middleware yourself.

```jsx React icon=code theme={"system"}
function App() {
  return (
    <InstantSearch
      // ...
      insights={true}
    >
      {/* Widgets */}
    </InstantSearch>
  );
}
```

If you had already set up the [`insights`](/doc/api-reference/widgets/insights/react) middleware in your code, you can now remove it and move its configuration to the [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights) option.

```diff React icon=code theme={"system"}
-import { createInsightsMiddleware } from 'instantsearch.js/es/middlewares';
-import { useInstantSearch } from 'react-instantsearch-hooks-web';
-import { useLayoutEffect } from 'react';
import { liteClient as algoliasearch } from 'algoliasearch/lite';

- function InsightsMiddleware() {
-   const { addMiddlewares } = useInstantSearch();
-
-   useLayoutEffect(() => {
-     const middleware = createInsightsMiddleware({
-       insightsClient: window.aa,
-       insightsInitParams: {
-         useCookie: false,
-       }
-     });
-
-     return addMiddlewares(middleware);
-   }, [addMiddlewares]);
-
-   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`](/doc/libraries/search-insights) for you so the [`insightsClient`](/doc/api-reference/widgets/insights/react#param-insights-client) option is no longer required.**

If you prefer loading it yourself, make sure to update [`search-insights`](/doc/libraries/search-insights) to at least v2.4.0 and forward the reference to [`insights`](/doc/api-reference/widgets/instantsearch/react#param-insights).

If you're using the UMD bundle with a `script` tag,
make sure to update the full code snippet (not just the version):

```html HTML icon=code-xml theme={"system"}
<script>
  var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@2.17.3/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.

```sh Command line icon=square-terminal theme={"system"}
npm install search-insights
```

<CodeGroup>
  ```js UMD theme={"system"}
  function App() {
    return (
      <InstantSearch 
        // ...
        insights={{
          insightsClient: window.aa,
        }
      >
        {/* ... */}
      </InstantSearch>
    );
  }
  ```

  ```js Module theme={"system"}
  import aa from 'search-insights';

  function App() {
    return (
      <InstantSearch 
        // ...
        insights={{
          insightsClient: aa,
        }
      >
        {/* ... */}
      </InstantSearch>
    );
  }
  ```
</CodeGroup>

Otherwise, you can [remove it and let InstantSearch handle it for you](#remove-search-insights).

### Remove `search-insights`

**Starting from v4.55.0, InstantSearch loads [`search-insights`](/doc/libraries/search-insights) for you from jsDelivr if not detected in the page.** If you've installed [`search-insights`](/doc/libraries/search-insights), you can now remove it.

If you're using the UMD bundle with a `script` tag, you can remove the snippet:

```diff HTML icon=code-xml theme={"system"}
- <script>
-   var ALGOLIA_INSIGHTS_SRC = "https://cdn.jsdelivr.net/npm/search-insights@2.17.3/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.

```sh Command line icon=square-terminal theme={"system"}
npm uninstall search-insights
```

Then you can remove the reference to [`search-insights`](/doc/libraries/search-insights) from your code:

<CodeGroup>
  ```diff UMD theme={"system"}
  function App() {
    return (
      <InstantSearch 
        // ...
        insights={{
  -       insightsClient: window.aa,
        }
      >
        {/* ... */}
      </InstantSearch>
    );
  }
  ```

  ```diff Module theme={"system"}
  - const aa = require('search-insights');
  // or
  - import aa from 'search-insights';
  function App() {
    return (
      <InstantSearch 
        // ...
        insights={{
  -       insightsClient: aa,
        }
      >
        {/* ... */}
      </InstantSearch>
    );
  }
  ```
</CodeGroup>

<Info>
  InstantSearch loads [`search-insights`](/doc/libraries/search-insights) from the jsDelivr CDN, which requires that your site or app allows script execution from foreign resources. Check the [security best practices](/doc/guides/security/security-best-practices) for recommendations.
</Info>

## Upgrade to Recommend

The [`RelatedProducts`](/doc/api-reference/widgets/related-products/react) widget is now available instead of the previous experimental widget `EXPERIMENTAL_RelatedItems`. To migrate, you change the widget name and update the props:

```diff React icon=code theme={"system"}
-import { EXPERIMENTAL_RelatedItems } from 'react-instantsearch';
+import { RelatedProducts } from 'react-instantsearch';

function App() {
  return (
    <InstantSearch
      indexName="instant_search"
      searchClient={searchClient}
    >
-      <EXPERIMENTAL_RelatedItems
-        hit={{
-          objectID: '123',
-          // ...
-        }}
-      />
-      <Hits />
+      <RelatedProducts
+        objectIDs={['123']}
+      />
    </InstantSearch>
  );
}
```
