Skip to main content
This is the React InstantSearch v7 documentation. If you’re upgrading from v6, see the upgrade guide. If you were using React InstantSearch Hooks, this v7 documentation applies—just check for necessary changes. To continue using v6, you can find the archived documentation.
If you’re using react-instantsearch 7.29.0 or earlier and want to display prompt suggestions before the first message, customize the chat layout with layoutComponent. If you only need prompt suggestions after the agent responds, use Agent Studio prompt suggestions.

Fetch starter prompts from a second agent

Use a second agent dedicated to generating starter prompts for the welcome screen. In this example, the second agent returns markdown grouped by headings:
Markdown
# Popular categories
Show best-selling headphones
Compare espresso machines

# Gift ideas
Find gifts under $50
Show travel accessories

Build a custom welcome screen

Fetch the starter prompts once, render them when messages.length === 0, and keep the built-in header, prompt, message list, and toggle button so the widget keeps its default behavior.
React
import React, { useEffect, useState } from "react";
import { liteClient as algoliasearch } from "algoliasearch/lite";
import { Chat, InstantSearch } from "react-instantsearch";

const appID = "ALGOLIA_APPLICATION_ID";
const apiKey = "ALGOLIA_SEARCH_ONLY_API_KEY";
const agentId = "YOUR_CHAT_AGENT_ID";
const navPromptId = "YOUR_STARTER_PROMPTS_AGENT_ID";

const searchClient = algoliasearch(appID, apiKey);

async function getInitPrompts() {
  const response = await fetch(
    `https://${appID}.algolia.net/agent-studio/1/agents/${navPromptId}/completions?stream=false&cache=true&compatibilityMode=ai-sdk-5`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Algolia-Application-Id": appID,
        "X-Algolia-API-Key": apiKey,
      },
      body: JSON.stringify({
        messages: [
          {
            role: "user",
            parts: [{ text: "Return starter prompts for the welcome screen." }],
          },
        ],
      }),
    }
  );

  const data = await response.json();
  const text = data.parts?.find((part) => part.type === "text")?.text;

  if (!text) {
    return [];
  }

  const groups = [];
  let currentGroup = null;

  for (const line of text.split("\n")) {
    const trimmedLine = line.trim();

    if (!trimmedLine) {
      continue;
    }

    if (trimmedLine.startsWith("# ")) {
      currentGroup = {
        title: trimmedLine.replace(/^#\s*/, ""),
        prompts: [],
      };
      groups.push(currentGroup);
      continue;
    }

    if (currentGroup) {
      currentGroup.prompts.push(trimmedLine);
    }
  }

  return groups;
}

const initPromptsPromise = getInitPrompts();

function ChatLayout({
  open,
  messages,
  maximized,
  headerComponent,
  messagesComponent,
  promptComponent,
  toggleButtonComponent,
  sendMessage,
}) {
  const [initPrompts, setInitPrompts] = useState([]);

  useEffect(() => {
    initPromptsPromise.then(setInitPrompts).catch(() => setInitPrompts([]));
  }, []);

  return (
    <>
      <div
        style={{
          position: "fixed",
          bottom: 16,
          right: 16,
        }}
      >
        {toggleButtonComponent}
      </div>

      {open && (
        <div
          style={{
            position: "fixed",
            bottom: maximized ? 0 : 80,
            right: maximized ? 0 : 16,
            width: maximized ? "100%" : 400,
            height: maximized ? "100%" : undefined,
            maxHeight: maximized ? "100%" : 600,
            display: "flex",
            flexDirection: "column",
            background: "white",
            borderRadius: maximized ? 0 : 12,
            boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
            overflow: "hidden",
          }}
        >
          {headerComponent}

          <div
            style={{
              flex: 1,
              overflow: "hidden",
              display: "flex",
              flexDirection: "column",
            }}
          >
            {messages.length === 0 ? (
              <div className="ais-ChatMessages">
                <div className="ais-ChatMessages-scroll ais-Scrollbar">
                  <div
                    style={{
                      marginBottom: "16px",
                    }}
                  >
                    <h2>How can we help?</h2>
                    <p>
                      Start with a prompt from the second agent, or type your own
                      question.
                    </p>

                    {initPrompts.map((group, groupIndex) => (
                      <section key={group.title || groupIndex}>
                        <div>
                          {group.title}
                        </div>

                        <div className="ais-ChatPromptSuggestions">
                          {group.prompts.map((prompt, promptIndex) => (
                            <button
                              key={promptIndex}
                              type="button"
                              className="ais-Button ais-Button--primary ais-Button--sm ais-ChatPromptSuggestions-suggestion"
                              onClick={() => sendMessage({ text: prompt })}
                            >
                              {prompt}
                            </button>
                          ))}
                        </div>
                      </section>
                    ))}
                  </div>
                </div>
              </div>
            ) : (
              messagesComponent
            )}
          </div>

          {promptComponent}
        </div>
      )}
    </>
  );
}

function App() {
  return (
    <InstantSearch searchClient={searchClient}>
      <Chat agentId={agentId} layoutComponent={ChatLayout} />
    </InstantSearch>
  );
}
Reuse headerComponent, messagesComponent, promptComponent, and toggleButtonComponent in your custom layout. This keeps the built-in chat behavior while replacing the initial empty state.
For more information, see the Chat API reference.
Last modified on April 22, 2026