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

# Server-side rendering

> Render your React InstantSearch app on your server.

<Note>
  This is the **React InstantSearch v7** documentation.
  If you're upgrading from v6, see the [upgrade guide](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-v6-to-react-instantsearch-v7).
  If you were using React InstantSearch Hooks,
  this v7 documentation applies—just check for [necessary changes](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-hooks-to-react-instantsearch-v7).
  To continue using v6, you can find the [archived documentation](https://algolia.com/old-docs/deprecated/instantsearch/react/v6/api-reference/instantsearch/).
</Note>

Server-side rendering (SSR) lets you generate HTML from InstantSearch components on the server.

Integrating SSR with React InstantSearch:

* **Improves general performance:** the browser directly loads with HTML containing search results, and React preserves the existing markup (hydration) instead of re-rendering everything.
* **Improves perceived performance:** users don't see a UI flash when loading the page, but directly the search UI. This can also improve your [Largest Contentful Paint](https://web.dev/lcp/) score.
* **Improves SEO:** the content is accessible to any search engine, even those that don't use JavaScript.

Here's the SSR flow for InstantSearch:

1. On the server, **retrieve the initial search results** of the current search state.
2. Then, on the server, **render these search results to HTML** and send the response to the browser.
3. Then, on the browser, **load the JavaScript code** for InstantSearch.
4. Then, on the browser, **hydrate the server-side rendered InstantSearch app.**

React InstantSearch is compatible with server-side rendering.
The library provides an API that works with any SSR solution.

For more information, see:

* [How to install React InstantSearch](/doc/guides/building-search-ui/installation/react)
* [Get started with React InstantSearch](/doc/guides/building-search-ui/getting-started/react)
* [`getServerState`](/doc/api-reference/widgets/server-state/react)
* [`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react)
* [`InstantSearchNext`](/doc/api-reference/widgets/instantsearch-next/react)

## With Next.js (App router)

As of Next.js 13, you can use the [App Router](https://nextjs.org/docs/app) to structure your app.
The App Router has a different approach to data fetching than the Pages Router,
which changes the approach to server-side rendering.

To support server-side rendering for the App Router, use the `react-instantsearch-nextjs` package.
It provides an `InstantSearchNext` component that replaces your [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) component.

### Install `react-instantsearch-nextjs`

First, make sure you have the correct dependencies installed:

* `react-instantsearch` >= `7.1.0`
* `next` >= `13.14.0`

Then, install the `react-instantsearch-nextjs` package:

<CodeGroup>
  ```sh npm theme={"system"}
  npm install react-instantsearch-nextjs
  ```

  ```sh yarn theme={"system"}
  yarn add react-instantsearch-nextjs
  ```
</CodeGroup>

### Usage

Your search component must be in its own file, and it shouldn't be named `page.js` or `page.tsx`.

To render the component in the browser and allow users to interact with it, include the "use client" directive at the top of your code.

```jsx React icon=code theme={"system"}
"use client"; // [!code ++]
import { liteClient as algoliasearch } from "algoliasearch/lite";
import { InstantSearch, SearchBox } from "react-instantsearch";

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

export function Search() {
  return (
    <InstantSearch indexName="YourIndexName" searchClient={searchClient}>
      <SearchBox />
      {/* other widgets */}
    </InstantSearch>
  );
}
```

Import the `InstantSearchNext` component from the `react-instantsearch-nextjs` package,
and replace the [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) component with it,
without changing the props.

```diff React icon=code theme={"system"}
'use client';
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import {
- InstantSearch,
  SearchBox,
} from 'react-instantsearch';
+import { InstantSearchNext } from 'react-instantsearch-nextjs';

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

export function Search() {
  return (
-   <InstantSearch indexName="YourIndexName" searchClient={searchClient}>
+   <InstantSearchNext indexName="YourIndexName" searchClient={searchClient}>
      <SearchBox />
      {/* other widgets */}
-   </InstantSearch>
+   </InstantSearchNext>
  );
}
```

To serve your search page at `/search`, create an `app/search` directory.
Inside it, create a `page.js` file (or `page.tsx` if you're using TypeScript).

Make sure to [configure your route segment to be dynamic](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes)
so that Next.js generates a new page for each request.

```jsx React icon=code theme={"system"}
// app/search/page.js or app/search/page.tsx
import { Search } from "./Search"; // change this with the path to your <Search> component

export const dynamic = "force-dynamic";

export default function Page() {
  return <Search />;
}
```

You can now visit `/search` to see your server-side rendered search page.

<Note>
  If you were previously using [`getServerState`](/doc/api-reference/widgets/server-state/react) in `getServerSideProps` with the Pages Router,
  remove any references to it. It's not needed with the App Router.
</Note>

### Composing hooks

Due to the way the App Router and `InstantSearchNext` are implemented,
you can't compose hooks the same way as you would normally do in React InstantSearch.
All React InstantSearch hooks have a `skipSuspense` option that you can set to `true` to skip the suspense behavior.
This should be done to all hooks but the last one in your custom component.

```jsx React icon=code theme={"system"}
export function MyCustomFilters() {
  const { items: brands } = useRefinementList(
    {
      attribute: 'brand',
    },
    { skipSuspense: true }
  );
  const { items: categories } = useRefinementList({ attribute: 'category' });

  return (
    // Render your filters here
  );
}
```

That way, categories will properly render on the server.

<Note>
  You will see a warning in the console if we detect that more widgets are rendered after suspense.
  This may be a false positive if you are using multi-index or dynamic widgets.
  You can ignore it in that case, and set the `ignoreMultipleHooksWarning` prop to `true` on `InstantSearchNext`.
</Note>

### Static site generation with dynamic route segments

You can generate a static version of your search page at build time using Next's [`generateStaticParams`](https://nextjs.org/docs/app/api-reference/functions/generate-static-params).
Static site generation (pre-rendering) and server-side rendering are essentially the same.
The key difference is timing:
static generation occurs at build time,
while server-side rendering occurs on request.

Adopt the same [usage recommendations](#usage) for both server-side rendering and static site generation.

```jsx React icon=code theme={"system"}
// app/[slug]/page.js or app/[slug]/page.tsx
export const dynamicParams = true;

export async function generateStaticParams() {
  const posts = await fetch(/* ... */).then((res) => res.json());
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export default async function Page({ params }) {
  const { slug } = await params;
  /* ... */
  return <Search /* ... */ />;
}
```

<Note>
  To statically render all paths at runtime,
  set the [`dynamic` route segment](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) to `'force-static'`.
</Note>

### Enabling routing

To enable routing, add a boolean `routing` prop to `InstantSearchNext`.

```jsx React icon=code theme={"system"}
function Search() {
  return (
    <InstantSearchNext
      indexName="YourIndexName"
      searchClient={searchClient}
      routing // [!code ++]
    >
      {/* widgets */}
    </InstantSearchNext>
  );
}
```

To customize the way InstantSearch maps the state to the route,
pass an object to `routing`, where `router` has the same options as [`history`](/doc/api-reference/widgets/history-router/react),
and `stateMapping` has the same options as the one in [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react).

```jsx React icon=code theme={"system"}
<InstantSearchNext
  routing={{
    router: {
      cleanUrlOnDispose: false,
      windowTitle(routeState) {
        const indexState = routeState.indexName || {};
        return indexState.query
          ? `MyWebsite - Results for: ${indexState.query}`
          : "MyWebsite - Results page";
      },
    },
  }}
>
  {/* widgets */}
</InstantSearchNext>;
```

### Dynamic routes

If you have a dynamic route, such as `/search/[brand]`, you may get CLS (Cumulative Layout Shift) issues when navigating between pages,
especially if you are using Next.js `Link` components.
To avoid this, use the `createInstantSearchNextInstance` function from the `react-instantsearch-router-nextjs` package to create an instance which will be shared across all pages.

```jsx React icon=code theme={"system"}
import {
  createInstantSearchNextInstance,
  InstantSearchNext,
} from "react-instantsearch-nextjs";

const searchClient = algoliasearch("ALGOLIA_APPLICATION_ID", "ALGOLIA_SEARCH_API_KEY");
const categoryPageInstance = createInstantSearchNextInstance();

export function BrandPageSearch({ brand }) {
  return (
    <InstantSearchNext
      indexName="YourIndexName"
      searchClient={searchClient}
      instance={categoryPageInstance}
    >
      <Configure filters={`brand:${brand}`} />
      {/* other widgets */}
    </InstantSearchNext>
  );
}
```

You can see a full example of this in the [Next.js dynamic routes example](https://github.com/algolia/instantsearch/blob/master/examples/react/next-app-router/app/\[category]/Search.tsx).

## With the Next.js Pages Router

Server-side rendering a page with the Pages Router in Next.js is split in two parts:
a function that returns data from the server, and a React component for the page that receives this data.

On the page, wrap the search experience with the [`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react) component.
This provider receives the server state and forwards it to the entire InstantSearch app.

### Server-side rendering

In Next's [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props),
you can use [`getServerState`](/doc/api-reference/widgets/server-state/react) to return the server state as a prop.
To support routing, you can use the `createInstantSearchRouterNext` function from the `react-instantsearch-router-nextjs` package.

<CodeGroup>
  ```jsx JavaScript theme={"system"}
  // index.js
  import { renderToString } from "react-dom/server";
  import { liteClient as algoliasearch } from "algoliasearch/lite";
  import {
    InstantSearch,
    InstantSearchSSRProvider,
    getServerState,
  } from "react-instantsearch";
  import { history } from "instantsearch.js/es/lib/routers/index.js";
  import singletonRouter from "next/router";
  import { createInstantSearchRouterNext } from "react-instantsearch-router-nextjs";

  const searchClient = algoliasearch("undefined", "undefined");

  export default function SearchPage({ serverState, serverUrl }) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch
          searchClient={searchClient}
          indexName="YourIndexName"
          routing={{
            router: createInstantSearchRouterNext({ singletonRouter, serverUrl }),
          }}
        >
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export async function getServerSideProps({ req }) {
    const protocol = req.headers.referer?.split["://"](0) || "https";
    const serverUrl = `${protocol}://${req.headers.host}${req.url}`;
    const serverState = await getServerState(
      <SearchPage serverUrl={serverUrl} />,
      { renderToString },
    );

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

  ```tsx TypeScript theme={"system"}
  // index.tsx
  import { renderToString } from 'react-dom/server';
  import { liteClient as algoliasearch } from 'algoliasearch/lite';
  import {
    InstantSearch,
    InstantSearchServerState,
    InstantSearchSSRProvider,
    getServerState,
  } from 'react-instantsearch';
  import { history } from 'instantsearch.js/es/lib/routers/index.js';
  import singletonRouter from 'next/router';
  import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';

  const searchClient = algoliasearch('undefined', 'undefined');

  type SearchPageProps = {
    serverState?: InstantSearchServerState;
    serverUrl: URL;
  };

  export default function SearchPage({ serverState, serverUrl }: SearchPageProps) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch
          searchClient={searchClient}
          indexName="YourIndexName"
          routing={{
            router: createInstantSearchRouterNext({ singletonRouter, serverUrl }),
          }}
        >
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export async function getServerSideProps({ req }) {
    const protocol = req.headers.referer?.split('://')[0] || 'https';
    const serverUrl = `${protocol}://${req.headers.host}${req.url}`;
    const serverState = await getServerState(
      <SearchPage serverUrl={serverUrl} />,
      { renderToString }
    );

    return {
      props: {
        serverState,
        serverUrl,
      },
    };
  }
  ```
</CodeGroup>

Check the complete [SSR example with Next.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/next?file=/pages/index.tsx).

### Static site generation

You can generate a static version of your search page at build time using Next's [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching/get-static-props). Static site generation (or pre-rendering) is essentially the same thing as [server-side rendering](/doc/guides/building-search-ui/going-further/server-side-rendering/react#server-side-rendering), except the latter happens at request time, while the former happens at build time.

You can use the same [`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react) and [`getServerState`](/doc/api-reference/widgets/server-state/react) APIs for both [server-side rendering](#server-side-rendering) and static site generation.

<CodeGroup>
  ```js JavaScript theme={"system"}
  // index.js
  import { renderToString } from "react-dom/server";
  import { liteClient as algoliasearch } from "algoliasearch/lite";
  import {
    InstantSearch,
    InstantSearchSSRProvider,
    getServerState,
  } from "react-instantsearch";

  const searchClient = algoliasearch("undefined", "undefined");

  export default function SearchPage({ serverState }) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch searchClient={searchClient} indexName="YourIndexName">
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export async function getStaticProps() {
    const serverState = await getServerState(<SearchPage />, { renderToString });
    return {
      props: {
        serverState,
      },
    };
  }
  ```

  ```tsx TypeScript theme={"system"}
  // index.tsx
  import { renderToString } from 'react-dom/server';
  import { liteClient as algoliasearch } from 'algoliasearch/lite';
  import {
    InstantSearch,
    InstantSearchServerState,
    InstantSearchSSRProvider,
    getServerState,
  } from 'react-instantsearch';

  const searchClient = algoliasearch('undefined', 'undefined');

  type SearchPageProps = {
    serverState?: InstantSearchServerState;
  };

  export default function SearchPage({ serverState }: SearchPageProps) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch searchClient={searchClient} indexName="YourIndexName">
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export async function getStaticProps() {
    const serverState = await getServerState(<SearchPage />, {
      renderToString
    });
    return {
      props: {
        serverState,
      },
    };
  }
  ```
</CodeGroup>

### Dynamic routes

If you want to generate pages dynamically—for example,
one for each brand—you can use Next's [`getStaticPaths`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths) API.

The following example uses [dynamic routes](https://nextjs.org/docs/routing/dynamic-routes) along with `getStaticPaths` to create one page per brand.

<CodeGroup>
  ```js JavaScript theme={"system"}
  // pages/brands/[brand].js
  import { renderToString } from "react-dom/server";
  import { liteClient as algoliasearch } from "algoliasearch/lite";
  import {
    InstantSearch,
    InstantSearchSSRProvider,
    SearchBox,
    Hits,
    Configure,
    getServerState,
  } from "react-instantsearch";

  const searchClient = algoliasearch("undefined", "undefined");

  export default function BrandPage({ brand, serverState }) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch searchClient={searchClient} indexName="YourIndexName">
          <Configure facetFilters={`brand:${brand}`} />
          <SearchBox />
          <Hits />
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export async function getStaticPaths() {
    return {
      // You can retrieve your brands from an API, a database, a file, etc.
      paths: [{ params: { brand: "Apple" } }, { params: { brand: "Samsung" } }],
      fallback: "blocking", // or `true` or `false`
    };
  }

  export async function getStaticProps({ params }) {
    if (!params) {
      return { notFound: true };
    }

    const serverState = await getServerState(<BrandPage brand={params.brand} />, {
      renderToString,
    });

    return {
      props: {
        brand: params.brand,
        serverState,
      },
    };
  }
  ```

  ```tsx TypeScript theme={"system"}
  // pages/brands/[brand].tsx
  import { renderToString } from 'react-dom/server';
  import { liteClient as algoliasearch } from 'algoliasearch/lite';
  import {
    InstantSearch,
    InstantSearchServerState,
    InstantSearchSSRProvider,
    SearchBox
    Hits,
    Configure,
    getServerState,
  } from 'react-instantsearch';
  import { GetStaticPathsResult, GetStaticPropsContext } from 'next';

  const searchClient = algoliasearch(
    'undefined',
    'undefined'
  );

  type BrandPageProps = {
    brand?: string;
    serverState?: InstantSearchServerState;
  };

  export default function BrandPage({ brand, serverState }: BrandPageProps) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch searchClient={searchClient} indexName="YourIndexName">
          <Configure facetFilters={`brand:${brand}`} />
          <SearchBox />
          <Hits />
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  type Params = {
    brand: string;
  };

  export function getStaticPaths(): GetStaticPathsResult<Params> {
    return {
      // You can retrieve your brands from an API, a database, a file, etc.
      paths: [{ params: { brand: 'Apple' } }, { params: { brand: 'Samsung' } }],
      fallback: 'blocking', // or `true` or `false`
    };
  }

  export async function getStaticProps({
    params,
  }: GetStaticPropsContext<Params>) {
    if (!params) {
      return { notFound: true };
    }

    const serverState = await getServerState(
      <BrandPage brand={params.brand} />,
      { renderToString }
    );

    return {
      props: {
        brand: params.brand,
        serverState,
      },
    };
  }
  ```
</CodeGroup>

If you have a reasonable amount of paths to generate and this number doesn't change much, you can generate them all at build time.
In this case, you can set `fallback: false`, which will serve a 404 page to users who try to visit a path that doesn't exist (for example, a brand that isn't in your dataset).

If there are many categories and generating them all significantly slows down your build,
you can pre-render only a subset of them (for example, the most popular ones) and generate the rest on the fly.

With `fallback: true`, whenever a user visits a path that doesn't exist, your `getStaticProps` code runs on the server and the page is generated once for all subsequent users.
Users see a loading screen that you can implement with [`router.isFallback`](https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-pages) until the page is ready.

With `fallback: 'blocking'`, the scenario is the same as with `fallback: true` but there's no loading screen. The server only returns the HTML once the page is generated.

## With Remix

[Remix](https://remix.run/) is a full-stack web framework that encourages usage of runtime servers, notably for server-side rendering.

Server-side rendering a page in Remix is split in two parts:

* A [loader](https://remix.run/docs/en/v1/api/conventions#loader) that returns data from the server,
* A React component for the page that receives this data.

In the page, you need to wrap the search experience with the [`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react) component.
This provider receives the server state and forwards it to the entire InstantSearch app.

In Remix' loader, you can use [`getServerState`](/doc/api-reference/widgets/server-state/react) to return the server state.
To support routing, forward the server's request URL to the [`history`](/doc/api-reference/widgets/history-router/js) router.

<CodeGroup>
  ```js JavaScript theme={"system"}
  // index.js
  import { renderToString } from "react-dom/server";
  import { liteClient as algoliasearch } from "algoliasearch/lite";
  import {
    InstantSearch,
    InstantSearchSSRProvider,
    getServerState,
  } from "react-instantsearch";
  import { history } from "instantsearch.js/cjs/lib/routers/index.js";

  import { json } from "@remix-run/node";
  import { useLoaderData } from "@remix-run/react";

  const searchClient = algoliasearch("undefined", "undefined");

  export async function loader({ request }) {
    const serverUrl = request.url;
    const serverState = await getServerState(<Search serverUrl={serverUrl} />, {
      renderToString,
    });

    return json({
      serverState,
      serverUrl,
    });
  }

  function Search({ serverState, serverUrl }) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch
          searchClient={searchClient}
          indexName="YourIndexName"
          routing={{
            router: history({
              getLocation() {
                if (typeof window === "undefined") {
                  return new URL(serverUrl);
                }

                return window.location;
              },
            }),
          }}
        >
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export default function HomePage() {
    const { serverState, serverUrl } = useLoaderData();

    return <Search serverState={serverState} serverUrl={serverUrl} />;
  }
  ```

  ```tsx TypeScript theme={"system"}
  // index.tsx
  import { renderToString } from 'react-dom/server';
  import { liteClient as algoliasearch } from 'algoliasearch/lite';
  import type { InstantSearchServerState } from 'react-instantsearch';
  import {
    InstantSearch,
    InstantSearchSSRProvider,
    getServerState,
  } from 'react-instantsearch';
  import { history } from 'instantsearch.js/cjs/lib/routers/index.js';

  import { json } from '@remix-run/node';
  import { useLoaderData } from '@remix-run/react';
  import type { LoaderFunction } from '@remix-run/node';

  const searchClient = algoliasearch('undefined', 'undefined');

  export const loader: LoaderFunction = async ({ request }) => {
    const serverUrl = request.url;
    const serverState = await getServerState(
      <Search serverUrl={serverUrl} />,
      { renderToString }
    );

    return json({
      serverState,
      serverUrl,
    });
  };

  type SearchProps = {
    serverState?: InstantSearchServerState;
    serverUrl?: string;
  };

  function Search({ serverState, serverUrl }: SearchProps) {
    return (
      <InstantSearchSSRProvider {...serverState}>
        <InstantSearch
          searchClient={searchClient}
          indexName="YourIndexName"
          routing={{
            router: history({
              getLocation() {
                if (typeof window === 'undefined') {
                  return new URL(serverUrl!) as unknown as Location;
                }

                return window.location;
              },
            }),
          }}
        >
          {/* Widgets */}
        </InstantSearch>
      </InstantSearchSSRProvider>
    );
  }

  export default function HomePage() {
    const { serverState, serverUrl } = useLoaderData() as SearchProps;

    return <Search serverState={serverState} serverUrl={serverUrl} />;
  }
  ```
</CodeGroup>

Check out the complete examples:

* [SSR with Remix](https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/react-instantsearch/remix?file=/app/routes/index.tsx).
* [SSR with Express](https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/ssr?file=/src/App.js).

## With a custom server

This guide shows how to server-side render your app with [Express](https://github.com/expressjs/express).
However, you can follow the same approach with any Node.js server.

The example in this guide has three files:

* `App.js`: the React component shared between the server and the browser
* `server.js`: the server entry to a Node.js HTTP server
* `browser.js`: the browser entry (which gets compiled to `assets/bundle.js`)

<Steps>
  <Step title="Create the React component">
    `App.js` is the main entry point to your React app. It exports an `App` component that you can render both on the server and in the browser.

    The [`InstantSearchSSRProvider`](/doc/api-reference/widgets/ssr-provider/react) component receives the server state and forwards it to [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react).

    <CodeGroup>
      ```js JavaScript theme={"system"}
      // App.js
      import { liteClient as algoliasearch } from "algoliasearch/lite";
      import React from "react";
      import { InstantSearch, InstantSearchSSRProvider } from "react-instantsearch";

      const searchClient = algoliasearch("undefined", "undefined");

      function App({ serverState }) {
        return (
          <InstantSearchSSRProvider {...serverState}>
            <InstantSearch indexName="YourIndexName" searchClient={searchClient}>
              {/* Widgets */}
            </InstantSearch>
          </InstantSearchSSRProvider>
        );
      }

      export default App;
      ```

      ```tsx TypeScript theme={"system"}
      // App.tsx
      import { liteClient as algoliasearch } from 'algoliasearch/lite';
      import React from 'react';
      import {
        InstantSearch,
        InstantSearchServerState,
        InstantSearchSSRProvider,
      } from 'react-instantsearch';

      const searchClient = algoliasearch('undefined', 'undefined');

      type AppProps = {
        serverState?: InstantSearchServerState;
      };

      function App({ serverState }: AppProps) {
        return (
          <InstantSearchSSRProvider {...serverState}>
            <InstantSearch indexName="YourIndexName" searchClient={searchClient}>
              {/* Widgets */}
            </InstantSearch>
          </InstantSearchSSRProvider>
        );
      }

      export default App;
      ```
    </CodeGroup>
  </Step>

  <Step title="Server-side render the page">
    When you receive the request on the server, you need to retrieve the server state so you can pass it down to `App`. This is what [`getServerState`](/doc/api-reference/widgets/server-state/react) does: it receives your InstantSearch app and computes a search state from it.

    In the `server.js` file:

    ```js JavaScript icon=code theme={"system"}
    import express from "express";
    import React from "react";
    import { renderToString } from "react-dom/server";
    import { getServerState } from "react-instantsearch";
    import App from "./App";

    const app = express();

    app.get("/", async (req, res) => {
      const serverState = await getServerState(<App />, { renderToString });
      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);
    ```

    Here, the server:

    1. **Retrieves the server state** with [`getServerState`](/doc/api-reference/widgets/server-state/react).
    2. **Renders the `App` as HTML** with this server state.
    3. **Sends the HTML** to the browser.

    Since you're sending plain HTML to the browser,
    you need a way to forward the server state object so you can reuse it in your InstantSearch app.
    To do so, you can serialize it and store it on the `window` object (here on the `__SERVER_STATE__` global), for later reuse in `browser.js`.
  </Step>

  <Step title="Hydrate the app in the browser">
    Once the browser has received HTML from the server, the final step is to connect this markup to the interactive app.
    This step is called [hydration](https://reactjs.org/docs/react-dom.html#hydrate).

    In the `browser.js` file:

    ```js JavaScript icon=code theme={"system"}
    import React from "react";
    import { hydrate } from "react-dom";
    import App from "./App";

    hydrate(
      <App serverState={window.__SERVER_STATE__} />,
      document.querySelector("#root"),
    );

    delete window.__SERVER_STATE__;
    ```

    Deleting `__SERVER_STATE__` from the global object allows the server state to be garbage collected.
  </Step>

  <Step title="Support routing">
    Server-side rendered search experiences should be able to generate HTML based on the current URL.
    You can use the [`history`](/doc/api-reference/widgets/history-router/js) router to synchronize [`InstantSearch`](/doc/api-reference/widgets/instantsearch/react) with the browser URL.

    <CodeGroup>
      ```diff JavaScript theme={"system"}
      // App.js
      import { liteClient as algoliasearch } from 'algoliasearch/lite';
      +import { history } from 'instantsearch.js/es/lib/routers';
      +// or cjs if you're running in a CommonJS environment
      +// import { history } from 'instantsearch.js/cjs/lib/routers';
      import React from 'react';
      import {
        InstantSearch,
        InstantSearchSSRProvider,
      } from 'react-instantsearch';

      const searchClient = algoliasearch('undefined', 'undefined');

      -function App({ serverState }) {
      +function App({ serverState, serverUrl }) {
        return (
          <InstantSearchSSRProvider {...serverState}>
            <InstantSearch
              indexName="YourIndexName"
              searchClient={searchClient}

      -       routing={{
      -         router: history({
      -           getLocation: () =>
      -             typeof window === 'undefined' ? serverUrl : window.location,
      -         }),
      -       }}
            >
              {/* Widgets */}
            </InstantSearch>
          </InstantSearchSSRProvider>
        );

      }

      export default App;
      ```

      ```diff TypeScript theme={"system"}
      // App.tsx
      import { liteClient as algoliasearch } from 'algoliasearch/lite';
      import React from 'react';
      +import { history } from 'instantsearch.js/es/lib/routers';
      +// or cjs if you're running in a CommonJS environment
      +// import { history } from 'instantsearch.js/cjs/lib/routers';
      import {
        InstantSearch,
        InstantSearchServerState,
        InstantSearchSSRProvider,
      } from 'react-instantsearch';

      const searchClient = algoliasearch('undefined', 'undefined');

      type AppProps = {
        serverState?: InstantSearchServerState;
      + serverUrl: URL;
      };

      -function App({ serverState }: AppProps) {
      +function App({ serverState, serverUrl }: AppProps) {
        return (
          <InstantSearchSSRProvider {...serverState}>
            <InstantSearch
              indexName="YourIndexName"
              searchClient={searchClient}
      +       routing={{
      +         router: history({
      +           getLocation: () =>
      +             typeof window === 'undefined' ? serverUrl : window.location,
      +         }),
      +       }}
            >
              {/* Widgets */}
            </InstantSearch>
          </InstantSearchSSRProvider>
        );
      }

      export default App;
      ```
    </CodeGroup>

    You can rely on `window.location` when rendering in the browser,
    and use the `location` provided by the server when rendering on the server.

    In the `server.js` file,  recreate the URL and pass it to the `App`:

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

    const app = express();

    app.get('/', async (req, res) => {
    + const serverUrl = new URL(
    +   `${req.protocol}://${req.get('host')}${req.originalUrl}`
    + );
    - const serverState = await getServerState(<App />, { renderToString });
    + const serverState = await getServerState(<App serverUrl={serverUrl} />, {
    +   renderToString,
    + });
    - const html = renderToString(<App serverState={serverState} />);
    + const html = renderToString(<App serverState={serverState} serverUrl={serverUrl} />);

      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);
    ```
  </Step>
</Steps>
