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

# Build an AI-powered search experience

> Combine the SearchBox, Chat, and autocomplete widgets to build an AI-powered search experience with React InstantSearch.

export const customLabel_0 = "in beta"

<div className="not-prose algolia-flavor-switcher">
  <div className="afs-dropdown">
    <div className="afs-trigger" role="button" tabIndex="0" aria-haspopup="listbox">
      <span className="afs-current">React</span>

      <svg className="afs-chevron lucide lucide-chevron-down" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <path d="m6 9 6 6 6-6" />
      </svg>
    </div>

    <ul className="afs-menu" role="listbox">
      <li role="option" aria-selected="false"><a className="afs-option" href="/doc/guides/building-search-ui/going-further/chat-customization/ai-search-experience/js"><span className="afs-option-name">JavaScript</span><span className="afs-option-lib">InstantSearch.js</span></a></li>
      <li role="option" aria-selected="true"><a className="afs-option is-current" href="/doc/guides/building-search-ui/going-further/chat-customization/ai-search-experience/react"><span className="afs-option-name">React</span><span className="afs-option-lib">React InstantSearch</span></a></li>
    </ul>
  </div>
</div>

<Callout icon="flask-conical" color="#14b8a6">
  This widget is **{customLabel_0 || "experimental"}** and is subject to change in minor versions.
</Callout>

Pair classic InstantSearch widgets with an [Agent Studio](/doc/guides/algolia-ai/agent-studio) chat agent to give users a conversational entry point alongside their search results. This guide wires together:

* An "AI Mode" button on the search box that opens a chat panel loaded with the user's query.
* The Chat widget itself, with hidden context to share session metadata.
* AI-generated prompt suggestions in the autocomplete drop-down menu.
* Streaming controls so you can stop or resume an in-flight response.

## Prerequisites

* An [Agent Studio agent ID](https://dashboard.algolia.com/generativeAi/agent-studio/agents).
* An `<InstantSearch>` root with an Algolia search client.
* `react-instantsearch` installed at a version that ships AI Mode (for example, the release from PR [`algolia/instantsearch#6982`](https://github.com/algolia/instantsearch/pull/6982) or later).

<Steps>
  <Step title="Mount the Chat widget">
    Add the [`<Chat>`](/doc/api-reference/widgets/chat/react) widget on the same `<InstantSearch>` instance as your search UI. The other widgets in this guide rely on it being present.

    ```jsx React icon=code theme={"system"}
    import { liteClient as algoliasearch } from "algoliasearch/lite";
    import { Chat, InstantSearch } from "react-instantsearch";

    const searchClient = algoliasearch("YourApplicationID", "YourSearchOnlyAPIKey");

    function App() {
      return (
        <InstantSearch indexName="instant_search" searchClient={searchClient}>
          <Chat agentId="YOUR_AGENT_ID" />
        </InstantSearch>
      );
    }
    ```

    `<Chat>` renders the chat panel but doesn't open it on its own. In this guide, the AI Mode button you add in the next step is the entry point. If you don't enable AI Mode, add the [`<ChatTrigger>`](/doc/api-reference/widgets/chat-trigger/react) widget instead, or `<Chat>` logs a development warning.
  </Step>

  <Step title="Enable AI Mode on the search box">
    Set [`aiMode`](/doc/api-reference/widgets/search-box/react#param-ai-mode) on `<SearchBox>` to render an AI Mode button next to the input. Clicking it opens the Chat widget and forwards the current query. AI Mode is a valid chat entry point, so you don't also need a [`<ChatTrigger>`](/doc/api-reference/widgets/chat-trigger/react) widget.

    ```jsx React icon=code theme={"system"}
    import { Chat, InstantSearch, SearchBox } from "react-instantsearch";

    function App() {
      return (
        <InstantSearch indexName="instant_search" searchClient={searchClient}>
          <SearchBox aiMode />
          <Chat agentId="YOUR_AGENT_ID" />
        </InstantSearch>
      );
    }
    ```
  </Step>

  <Step title="Show prompt suggestions in autocomplete (optional)">
    If you use [`<EXPERIMENTAL_Autocomplete>`](/doc/api-reference/widgets/autocomplete/react), surface AI-generated prompt suggestions alongside hits. See [`showPromptSuggestions`](/doc/api-reference/widgets/autocomplete/react#param-show-prompt-suggestions) for the full configuration surface.

    ```jsx React icon=code theme={"system"}
    <EXPERIMENTAL_Autocomplete
      showPromptSuggestions={{
        indexName: "prompt_suggestions",
      }}
      indices={[
        {
          indexName: "instant_search",
          templates: {
            item: ({ item }) => <div>{item.name}</div>,
          },
        },
      ]}
    />
    ```

    Selecting a prompt suggestion opens the Chat widget on the same index and sends the suggestion as the first message.
  </Step>

  <Step title="Pass session metadata with hidden context">
    Use the [`context`](/doc/api-reference/widgets/chat/react#param-context) prop to share metadata with the agent on every message. The widget serializes the value as JSON and adds it to the user message as a hidden text part. Users don't see it in the chat UI, but the agent does.

    <Warning>
      The widget sends `context` to the agent in plain text. Don't put secrets, access tokens, or personally identifiable information you don't intend to share with the model in this field.
    </Warning>

    <CodeGroup>
      ```jsx Static object theme={"system"}
      <Chat
        agentId="YOUR_AGENT_ID"
        context={{ locale: "en", segment: "logged-in" }}
      />
      ```

      ```jsx Function theme={"system"}
      <Chat
        agentId="YOUR_AGENT_ID"
        context={() => ({
          locale: navigator.language,
          currentPage: window.location.pathname,
        })}
      />
      ```
    </CodeGroup>
  </Step>

  <Step title="Handle the streaming lifecycle">
    Listen for response completion with [`onFinish`](/doc/api-reference/widgets/chat/react#param-on-finish), and use the `useChat` hook to expose Stop and resume controls. See [Streaming and resumption](/doc/api-reference/widgets/chat/react#streaming-and-resumption) on the Chat widget reference for the full status surface.

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

    function StopButton({ agentId }) {
      const { status, stop } = useChat({ agentId });

      if (status !== "streaming") {
        return null;
      }

      return <button onClick={() => stop()}>Stop</button>;
    }

    function App() {
      return (
        <InstantSearch indexName="instant_search" searchClient={searchClient}>
          <SearchBox aiMode />
          <Chat
            agentId="YOUR_AGENT_ID"
            resume
            onFinish={({ message, isError }) => {
              if (!isError) {
                analytics.track("chat_message_completed", { id: message.id });
              }
            }}
          />
          <StopButton agentId="YOUR_AGENT_ID" />
        </InstantSearch>
      );
    }
    ```

    Set [`resume`](/doc/api-reference/widgets/chat/react#param-resume) to reconnect to an in-flight assistant response after a page reload.
  </Step>
</Steps>

## End-to-end example

```jsx React icon=code expandable theme={"system"}
import React from "react";
import { liteClient as algoliasearch } from "algoliasearch/lite";
import {
  Chat,
  EXPERIMENTAL_Autocomplete,
  Hits,
  InstantSearch,
  SearchBox,
  useChat,
} from "react-instantsearch";

const searchClient = algoliasearch("YourApplicationID", "YourSearchOnlyAPIKey");
const agentId = "YOUR_AGENT_ID";

function StopButton() {
  const { status, stop } = useChat({ agentId });
  if (status !== "streaming") return null;
  return <button onClick={() => stop()}>Stop</button>;
}

function Hit({ hit }) {
  return <article>{hit.name}</article>;
}

export default function App() {
  return (
    <InstantSearch indexName="instant_search" searchClient={searchClient}>
      <SearchBox aiMode />
      <EXPERIMENTAL_Autocomplete
        showPromptSuggestions={{ indexName: "prompt_suggestions" }}
        indices={[
          {
            indexName: "instant_search",
            templates: { item: ({ item }) => <div>{item.name}</div> },
          },
        ]}
      />
      <Hits hitComponent={Hit} />
      <Chat
        agentId={agentId}
        resume
        context={() => ({
          locale: navigator.language,
          currentPage: window.location.pathname,
        })}
        onFinish={({ message, isError }) => {
          if (!isError) {
            analytics.track("chat_message_completed", { id: message.id });
          }
        }}
      />
      <StopButton />
    </InstantSearch>
  );
}
```

## Related

* [SearchBox widget reference](/doc/api-reference/widgets/search-box/react)
* [Chat widget reference](/doc/api-reference/widgets/chat/react)
* [Autocomplete widget reference](/doc/api-reference/widgets/autocomplete/react)
* [Agent Studio](/doc/guides/algolia-ai/agent-studio)
* [Add a legal notice to the chat widget](/doc/guides/building-search-ui/going-further/chat-customization/legal-notice/react)
* [Customize the chat loading message](/doc/guides/building-search-ui/going-further/chat-customization/loading-message/react)
* [Show starter prompts on the chat welcome screen](/doc/guides/building-search-ui/going-further/chat-customization/welcome-screen/react)
