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

# promptSuggestions

> Displays AI-generated prompt suggestions for the current page, built with Algolia Agent Studio.

export const customLabel_0 = undefined

export const FlavorSwitcher = ({current, baseHref = "", options = [], label = "InstantSearch framework"}) => {
  if (options.length === 0) {
    return <div className="not-prose" role="alert" style={{
      margin: "0.25rem 0 1.5rem",
      padding: "0.75rem",
      border: "1px solid #f59e0b",
      borderRadius: "0.625rem",
      color: "inherit",
      fontSize: "0.875rem"
    }}>
        FlavorSwitcher requires at least one option.
      </div>;
  }
  const selected = options.find(option => option.value === current) ?? options[0];
  return <div className="not-prose mint-flavor-switcher">
      <style>{`
        .mint-flavor-switcher {
          --mfs-bg: #ffffff;
          --mfs-bg-hover: #f4f4f5;
          --mfs-bg-current: #eef2ff;
          --mfs-border: #d4d4d8;
          --mfs-fg: #18181b;
          --mfs-muted: #71717a;
          --mfs-accent: #4f46e5;
          position: relative;
          width: min(100%, 19rem);
          margin: 0.25rem 0 1.5rem;
          color: var(--mfs-fg);
          font-size: 0.875rem;
          line-height: 1.25rem;
        }

        .dark .mint-flavor-switcher {
          --mfs-bg: #18181b;
          --mfs-bg-hover: #27272a;
          --mfs-bg-current: #272747;
          --mfs-border: #3f3f46;
          --mfs-fg: #fafafa;
          --mfs-muted: #a1a1aa;
          --mfs-accent: #a5b4fc;
        }

        .mint-flavor-switcher details {
          position: relative;
        }

        .mint-flavor-switcher summary {
          display: flex;
          min-height: 2.75rem;
          box-sizing: border-box;
          align-items: center;
          justify-content: space-between;
          gap: 0.75rem;
          padding: 0.625rem 0.75rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          color: var(--mfs-fg);
          cursor: pointer;
          font-weight: 600;
          list-style: none;
          transition: border-color 150ms ease, box-shadow 150ms ease;
        }

        .mint-flavor-switcher summary::-webkit-details-marker {
          display: none;
        }

        .mint-flavor-switcher summary:hover {
          border-color: var(--mfs-accent);
        }

        .mint-flavor-switcher summary:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: 2px;
        }

        .mint-flavor-switcher__label {
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .mint-flavor-switcher__chevron {
          flex: none;
          transition: transform 150ms ease;
        }

        .mint-flavor-switcher details[open] .mint-flavor-switcher__chevron {
          transform: rotate(180deg);
        }

        .mint-flavor-switcher__menu {
          position: absolute;
          z-index: 50;
          top: calc(100% + 0.375rem);
          left: 0;
          width: 100%;
          box-sizing: border-box;
          margin: 0;
          padding: 0.375rem;
          border: 1px solid var(--mfs-border);
          border-radius: 0.625rem;
          background: var(--mfs-bg);
          box-shadow: 0 12px 30px rgb(0 0 0 / 16%);
          list-style: none;
        }

        .mint-flavor-switcher__menu li {
          margin: 0;
          padding: 0;
        }

        .mint-flavor-switcher__option {
          display: grid;
          gap: 0.125rem;
          padding: 0.625rem 0.75rem;
          border-radius: 0.4rem;
          color: var(--mfs-fg);
          text-decoration: none;
        }

        .mint-flavor-switcher__option:hover {
          background: var(--mfs-bg-hover);
        }

        .mint-flavor-switcher__option:focus-visible {
          outline: 2px solid var(--mfs-accent);
          outline-offset: -2px;
        }

        .mint-flavor-switcher__option[aria-current="page"] {
          background: var(--mfs-bg-current);
          color: var(--mfs-accent);
        }

        .mint-flavor-switcher__name {
          font-weight: 600;
        }

        .mint-flavor-switcher__description {
          color: var(--mfs-muted);
          font-size: 0.8125rem;
        }

        @media (prefers-reduced-motion: reduce) {
          .mint-flavor-switcher summary,
          .mint-flavor-switcher__chevron {
            transition: none;
          }
        }
      `}</style>

      <details>
        <summary aria-label={`${label}: ${selected.label}`}>
          <span className="mint-flavor-switcher__label">{selected.label}</span>
          <svg className="mint-flavor-switcher__chevron" width="18" height="18" 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>
        </summary>

        <ul className="mint-flavor-switcher__menu" aria-label={label}>
          {options.map(option => {
    const isCurrent = option.value === selected.value;
    const href = option.href ?? `${baseHref.replace(/\/$/, "")}/${encodeURIComponent(option.value)}`;
    return <li key={option.value}>
                <a className="mint-flavor-switcher__option" href={href} aria-current={isCurrent ? "page" : undefined}>
                  <span className="mint-flavor-switcher__name">
                    {option.label}
                  </span>
                  {option.description ? <span className="mint-flavor-switcher__description">
                      {option.description}
                    </span> : null}
                </a>
              </li>;
  })}
        </ul>
      </details>
    </div>;
};

<div className="mint-flavor-switcher-slot not-prose">
  <FlavorSwitcher
    current="js"
    baseHref="/doc/api-reference/widgets/prompt-suggestions"
    options={[
{ value: "js", label: "JavaScript", description: "InstantSearch.js" },
{ value: "react", label: "React", description: "React InstantSearch" },
]}
  />
</div>

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

For more information, see [Agent Studio](/doc/guides/algolia-ai/agent-studio).

```ts Signature theme={"system"}
promptSuggestions({
  container: string | HTMLElement,
  configurationId: string,
  // Required parameter, either one of
  agentId?: string,
  transport?: object,
  // Optional parameters
  context?: object | function,
  transformHits?: function,
  transformItems?: function,
  onSuggestionClick?: function,
  templates?: object,
  cssClasses?: object,
  translations?: object,
});
```

## Import

<CodeGroup>
  ```js Package manager theme={"system"}
  import { promptSuggestions } from 'instantsearch.js/es/widgets';
  ```

  ```js CDN theme={"system"}
  const { promptSuggestions } = instantsearch.widgets;
  ```
</CodeGroup>

## About this widget

Use the `promptSuggestions` widget to display AI-generated prompt suggestions as clickable pills.
An Agent Studio agent generates the suggestions from the current search context: the query, the active filters, and a sample of the results.
When users click a suggestion, the widget opens the [`chat`](/doc/api-reference/widgets/chat/js) widget on the same index and submits the prompt.

The widget fetches suggestions when it mounts, and fetches them again when the query, filters, or results change.
Suggestions stream in progressively, and a skeleton placeholder shows while the first suggestions load.

These suggestions differ from the [follow-up prompt suggestions](/doc/guides/algolia-ai/agent-studio/how-to/agent-configuration#follow-up-prompt-suggestions) that appear after each agent response inside the chat.

See also: [Agent Studio](/doc/guides/algolia-ai/agent-studio)

<Note>
  The default click behavior needs a [`chat`](/doc/api-reference/widgets/chat/js) widget on the same index. Without one, the widget logs a development warning when users click a suggestion. To handle clicks yourself, use [`onSuggestionClick`](#param-on-suggestion-click).
</Note>

## Examples

```js JavaScript icon=code theme={"system"}
promptSuggestions({
  container: '#prompt-suggestions',
  agentId: '8f7c4a2d-3b1e-4d5f-9a6c-e2b1f5d0c3e9',
  configurationId: 'CONFIGURATION_ID',
});

chat({
  container: '#chat',
  agentId: '8f7c4a2d-3b1e-4d5f-9a6c-e2b1f5d0c3e9',
});
```

## Options

<ParamField body="container" type="string | HTMLElement" required>
  The CSS Selector or `HTMLElement` to insert the widget into.

  <CodeGroup>
    ```js string theme={"system"}
    promptSuggestions({
      container: '#prompt-suggestions',
    });
    ```

    ```js HTMLElement theme={"system"}
    promptSuggestions({
      container: document.querySelector('#prompt-suggestions'),
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="configurationId" type="string" required>
  The identifier of the prompt configuration that generates the suggestions. Create prompt configurations in the **Components** section of your agent in the Agent Studio dashboard.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    configurationId: 'CONFIGURATION_ID',
  });
  ```
</ParamField>

<ParamField body="agentId" type="string">
  The unique identifier of the agent to connect to. You can find the `agentId` in the [Agent Studio dashboard](https://dashboard.algolia.com/generativeAi/agent-studio/agents). Required unless you provide a custom [`transport`](#param-transport).

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    agentId: '8f7c4a2d-3b1e-4d5f-9a6c-e2b1f5d0c3e9',
  });
  ```
</ParamField>

<ParamField body="transport" type="object">
  A custom transport object to handle the communication between the widget and your own backend. When set, `agentId` and the search client credentials are ignored.

  The object accepts the following properties:

  * `api` (`string`). The endpoint URL to send suggestion requests to.
  * `headers` (`Record<string, string>`). Headers to send with each request.
  * `prepareSendMessagesRequest` (`function`). Transforms the request body before it's sent. Receives the body object and returns `{ body }`.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    transport: {
      api: 'https://api.example.com/api/v1/suggestions',
      headers: {
        'X-Api-Version': '2025-01-01',
      },
    },
  });
  ```
</ParamField>

<ParamField body="context" type="object | function">
  Explicit page context to send to the agent instead of the automatically extracted search context.

  By default, the widget sends the current query, the active filters, and a sample of the results.
  Set `context` to replace this with your own data, for example the product record on a product detail page.
  `context` can be a static object or a function that returns an object for each fetch.

  When you set `context`, the widget fetches suggestions even without search results, and `transformHits` is ignored.

  <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>
    ```js Static object theme={"system"}
    promptSuggestions({
      // ...
      context: {
        productName: 'Wireless headphones',
        category: 'Audio',
        price: 99,
      },
    });
    ```

    ```js Function theme={"system"}
    promptSuggestions({
      // ...
      context: () => ({
        currentPage: window.location.pathname,
      }),
    });
    ```
  </CodeGroup>
</ParamField>

<ParamField body="transformHits" type="function">
  A function that receives the current results and returns the subset (or reshaped objects) sent to the agent as context.
  By default, the widget sends the first five hits with internal metadata (attributes prefixed with `_`) removed.
  This option is ignored when you set [`context`](#param-context).

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    transformHits: (hits) =>
      hits.slice(0, 3).map((hit) => ({
        name: hit.name,
        brand: hit.brand,
        price: hit.price,
      })),
  });
  ```
</ParamField>

<ParamField body="transformItems" type="function">
  A function that receives the generated suggestions and returns the list to display.
  The second argument contains the current `query` and `results`.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    transformItems: (items, { query }) =>
      items.slice(0, 2).map((item) => item.toUpperCase()),
  });
  ```
</ParamField>

<ParamField body="onSuggestionClick" type="function">
  A function to override the default click behavior (sending the prompt to the chat widget).
  It receives the clicked prompt and a helpers object with `sendToChat`, so you can run custom logic (for example, analytics) and then fall through to the default behavior.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    onSuggestionClick: (prompt, { sendToChat }) => {
      analytics.track('suggestion_clicked', { prompt });
      sendToChat(prompt);
    },
  });
  ```
</ParamField>

<ParamField body="templates" type="object">
  The [templates](#templates) to use for the widget.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    templates: {
      // ...
    },
  });
  ```
</ParamField>

<ParamField body="cssClasses" type="object">
  The [CSS classes you can override](/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js#style-your-widgets):

  * `root`. The root element of the widget.
  * `header`. The header element.
  * `headerTitle`. The header title element.
  * `suggestion`. Each suggestion pill.
  * `skeleton`. The skeleton container shown while loading.
  * `skeletonItem`. Each skeleton placeholder pill.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    cssClasses: {
      root: 'MyCustomPromptSuggestions',
      suggestion: [
        'MyCustomPromptSuggestionsPill',
        'MyCustomPromptSuggestionsPill--subclass',
      ],
    },
  });
  ```
</ParamField>

<ParamField body="translations" type="object">
  A dictionary of translations to customize the UI text and support internationalization.

  * `headerTitle`. The title displayed in the header.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    translations: {
      headerTitle: 'Ask about these results',
    },
  });
  ```
</ParamField>

## Templates

You can customize parts of a widget’s UI using the Templates API.

Each template includes an `html` function,
which you can use as a [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates).
This function safely renders templates as HTML strings and works directly in the browser—no build step required.
For details, see [Templating your UI](/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js/#templating-your-ui).

<Note>
  The `html` function is available in InstantSearch.js version 4.46.0 or later.
</Note>

<ParamField body="layout" type="function">
  A template to replace the default pills layout with custom markup. The template owns the full rendering: the list, the loading state, and the click handlers. It receives:

  * `suggestions`. The generated prompt strings.
  * `isLoading`. Whether the widget is fetching suggestions.
  * `onSuggestionClick`. The click handler for a suggestion.
  * `isChatBusy`. Whether the chat widget is streaming a response.

  ```js JavaScript icon=code expandable theme={"system"}
  promptSuggestions({
    // ...
    templates: {
      layout({ suggestions, isLoading, onSuggestionClick, isChatBusy }, { html }) {
        if (isLoading) {
          return html`<span>Generating suggestions...</span>`;
        }

        return html`
          <ul>
            ${suggestions.map(
              (suggestion) =>
                html`<li>
                  <button
                    disabled=${isChatBusy}
                    onClick=${() => onSuggestionClick(suggestion)}
                  >
                    ${suggestion}
                  </button>
                </li>`,
            )}
          </ul>
        `;
      },
    },
  });
  ```
</ParamField>

<ParamField body="header" type="function | false">
  A template to replace the default header. Set it to `false` to hide the header. It receives the `classNames` and `translations` objects.

  ```js JavaScript icon=code theme={"system"}
  promptSuggestions({
    // ...
    templates: {
      header({ translations }, { html }) {
        return html`<h4>${translations.headerTitle}</h4>`;
      },
    },
  });
  ```
</ParamField>

## HTML output

```html HTML icon=code-xml theme={"system"}
<div class="ais-PromptSuggestions">
  <div class="ais-PromptSuggestions-header">
    <span class="ais-PromptSuggestions-headerTitle">Suggestions</span>
  </div>
  <button class="ais-Button ais-Button--primary ais-Button--sm ais-PromptSuggestions-suggestion">
    What are the best headphones under $100?
  </button>
  <button class="ais-Button ais-Button--primary ais-Button--sm ais-PromptSuggestions-suggestion">
    Which of these have noise cancellation?
  </button>
</div>
```

While the first suggestions load, the widget renders skeleton placeholders instead of the pills:

```html HTML icon=code-xml theme={"system"}
<div class="ais-PromptSuggestions">
  <div class="ais-PromptSuggestions-skeleton">
    <div class="ais-PromptSuggestions-skeletonItem"></div>
    <div class="ais-PromptSuggestions-skeletonItem"></div>
    <div class="ais-PromptSuggestions-skeletonItem"></div>
  </div>
</div>
```

## Connector render state

Use `connectPromptSuggestions` to build a fully custom UI. Its render state exposes:

* `suggestions` (`string[]`). The generated prompt strings.
* `isLoading` (`boolean`). Whether the widget is fetching suggestions.
* `onSuggestionClick` (`(prompt: string) => void`). The default click handler, which sends the prompt to the chat.
* `sendToChat` (`(prompt: string) => boolean`). Sends the prompt to the `chat` widget on the same index, with the page context attached as [per-turn context](/doc/guides/algolia-ai/agent-studio/how-to/turn-context). Returns `true` if the chat received it.
* `refresh` (`() => void`). Fetches new suggestions for the current search state.
* `isChatBusy` (`boolean`). Whether the chat widget is streaming a response. Use it to set the `disabled` attribute on your suggestion buttons.

```js JavaScript icon=code expandable theme={"system"}
import { connectPromptSuggestions } from 'instantsearch.js/es/connectors';

const renderPromptSuggestions = (renderOptions, isFirstRender) => {
  const { suggestions, isLoading, onSuggestionClick, isChatBusy } = renderOptions;
  const container = document.querySelector('#prompt-suggestions');

  if (isLoading) {
    container.textContent = 'Generating suggestions...';
    return;
  }

  container.innerHTML = '';
  suggestions.forEach((suggestion) => {
    const button = document.createElement('button');
    button.textContent = suggestion;
    button.disabled = isChatBusy;
    button.addEventListener('click', () => onSuggestionClick(suggestion));
    container.appendChild(button);
  });
};

const customPromptSuggestions = connectPromptSuggestions(
  renderPromptSuggestions,
);

search.addWidgets([
  customPromptSuggestions({
    agentId: '8f7c4a2d-3b1e-4d5f-9a6c-e2b1f5d0c3e9',
    configurationId: 'CONFIGURATION_ID',
  }),
]);
```
