# @aiconnect/react (/docs/reference/react)

`@aiconnect/react` holds the `AiConnect` client for a React tree, renders the connect window, and runs chats on the visitor's AI. It needs React 19 or later.

The tables on this page are generated from the package's TypeScript source each time the docs are built. Open a row to see its full type. The types of `@aiconnect/core` they name are in the [core reference](/docs/reference/core).

```ts
import { AiConnectProvider, builtInMessages, useAiChat, useAiConnect } from '@aiconnect/react';
import type {
  AiConnectProviderProps, ChatTurn, Locale, PickerMessages, ToolActivity, UseAiChatOptions,
} from '@aiconnect/react';
import '@aiconnect/react/styles.css';
```

## styles.css [#stylescss]

```ts
import '@aiconnect/react/styles.css';
```

The connect window's stylesheet, with the `--aic-*` tokens of `@aiconnect/design` included. Import it once. The [theming guide](/docs/guides/theming) lists the tokens.

## AiConnectProvider [#aiconnectprovider]

Holds the `AiConnect` client for a React tree and renders the connect window when it is open.

### AiConnectProviderSignature

| Prop                | Type                                                   | Description |
| ------------------- | ------------------------------------------------------ | ----------- |
| `AiConnectProvider` | `(props: AiConnectProviderProps) => React.JSX.Element` |             |

The provider reads the client's state with `useSyncExternalStore`. The server render and the hydration see `{ status: 'checking' }`, and the browser's state follows. The connect window closes on its own when the state becomes `connected`.

### AiConnectProviderProps [#aiconnectproviderprops]

### AiConnectProviderProps

| Prop              | Type                                | Description |
| ----------------- | ----------------------------------- | ----------- |
| `client`          | `AiConnect`                         |             |
| `locale?`         | `"en" \| "fr" \| "he" \| undefined` |             |
| `messages?`       | `PickerMessages \| undefined`       |             |
| `moduleMessages?` | `ModuleMessages \| undefined`       |             |
| `children`        | `ReactNode`                         |             |

- **client.** The `AiConnect` client. Create one per page and keep it stable across renders.
- **locale.** The language of the built in messages: one of the [locales](#locale). The default is `en`.
- **messages.** Your own copy for the window. When given, it replaces the built in messages of `locale` as a whole.
- **children.** Your app. The connect window renders right after it, inside the provider's position in the page.

```tsx
const client = new AiConnect();

export function Root() {
  return (
    <AiConnectProvider client={client} locale="fr">
      <App />
    </AiConnectProvider>
  );
}
```

## useAiConnect [#useaiconnect]

Reads the connection and controls the connect window. It throws "useAiConnect must be used inside AiConnectProvider" outside the provider.

### UseAiConnect

| Prop           | Type                          | Description |
| -------------- | ----------------------------- | ----------- |
| `useAiConnect` | `() => AiConnectContextValue` |             |

It returns this value. Its type is not exported.

### UseAiConnectValue

| Prop             | Type              | Description |
| ---------------- | ----------------- | ----------- |
| `client`         | `AiConnect`       |             |
| `state`          | `ConnectionState` |             |
| `messages`       | `PickerMessages`  |             |
| `moduleMessages` | `ModuleMessages`  |             |
| `isPickerOpen`   | `boolean`         |             |
| `openPicker`     | `() => void`      |             |
| `closePicker`    | `() => void`      |             |

- **client.** The client given to the provider, for `connect()`, `disconnect()` and `subscribe()`.
- **state.** The current `ConnectionState`.
- **messages.** The messages the window uses now.
- **isPickerOpen**, **openPicker**, **closePicker.** Whether the connect window is open, and the functions that open and close it.

The connect window shows the step that matches the state: a search for the wallet in `checking`, the install text and the button to the AI Wallet page in `noWallet`, the connect button in `disconnected`, a waiting step while the wallet's window is open in `connecting`, and the reason with a retry button in `error`. Its help link shows what connecting means for the visitor. Escape and a click on the backdrop close it.

```tsx
function AiModeButton() {
  const { state, openPicker } = useAiConnect();
  if (state.status === 'connected') {
    return <span>Answered by your {state.providerName} account</span>;
  }
  return <button type="button" onClick={openPicker}>AI mode</button>;
}
```

## useAiChat [#useaichat]

A conversation with the visitor's own model, using your tools.

### UseAiChat

| Prop        | Type       | Description |
| ----------- | ---------- | ----------- |
| `useAiChat` | `function` |             |

It returns this value:

### UseAiChatValue

| Prop        | Type                                  | Description |
| ----------- | ------------------------------------- | ----------- |
| `turns`     | `ChatTurn[]`                          |             |
| `send`      | `(question: string) => Promise<void>` |             |
| `stop`      | `() => void \| undefined`             |             |
| `reset`     | `() => void`                          |             |
| `busy`      | `boolean`                             |             |
| `error`     | `string \| null`                      |             |
| `connected` | `boolean`                             |             |

- **turns.** One entry per question, updated as the answer streams and the tools run.
- **send(question).** Trims the question and runs a conversation on it with the whole transcript so far, tool results included. It does nothing when the site is not connected or the question is blank. Calls are not queued, so disable your send button while `busy` is true. A conversation that fails or is stopped leaves the transcript as it was, and its turn stays in `turns`.
- **stop().** Aborts the running conversation. The wallet stops the answer, the turn keeps the text streamed so far, and `error` stays empty.
- **reset().** Aborts the running conversation, clears the turns, the error and the transcript, and starts again from the current `system`.
- **busy.** True while a conversation runs.
- **error.** The message of the last failed conversation, cleared by the next `send` and by `reset`.
- **connected.** True when the connection state is `connected`.

The hook must render inside `AiConnectProvider`. It reads `system` when it first renders and on `reset()`. Pass a `tools` instance that stays the same across renders, created at module level or with `useMemo`.

```tsx
const tools = new McpToolSource('/api/mcp', 'Bookshop');

function Chat() {
  const { turns, send, stop, busy, error } = useAiChat({ system: 'You recommend books from this catalog.', tools });
  return (
    <>
      {turns.map((turn, index) => <p key={index}>{turn.answer}</p>)}
      {error ? <p role="alert">{error}</p> : null}
      <button type="button" disabled={busy} onClick={() => send('A calm book for tonight')}>Ask</button>
      {busy ? <button type="button" onClick={stop}>Stop</button> : null}
    </>
  );
}
```

## UseAiChatOptions [#useaichatoptions]

### UseAiChatOptions

| Prop     | Type                      | Description |
| -------- | ------------------------- | ----------- |
| `system` | `string`                  |             |
| `tools?` | `ToolSource \| undefined` |             |

- **system.** The system prompt at the head of the transcript.
- **tools.** The tool source, such as an `McpToolSource`. Without it, the model answers each question once.

```ts
const options: UseAiChatOptions = { system: SYSTEM_PROMPT, tools: new McpToolSource('/api/mcp') };
```

## ChatTurn and ToolActivity [#chatturn-and-toolactivity]

### ChatTurn [#chatturn]

### ChatTurn

| Prop       | Type             | Description |
| ---------- | ---------------- | ----------- |
| `question` | `string`         |             |
| `answer`   | `string`         |             |
| `tools`    | `ToolActivity[]` |             |

### ToolActivity [#toolactivity]

### ToolActivity

| Prop     | Type             | Description |
| -------- | ---------------- | ----------- |
| `call`   | `ToolCall`       |             |
| `result` | `string \| null` |             |

- **question.** The visitor's question, trimmed.
- **answer.** The text the model streamed during the turn, across every step of the tool loop.
- **tools.** Each tool call of the turn in order, as a `ToolActivity`. Its `result` is `null` while the tool runs, then holds the raw text the tool returned, or `Tool error: ` followed by the reason.

```tsx
function ToolStatus({ turn }: { turn: ChatTurn }) {
  const running: ToolActivity | undefined = turn.tools.find((activity) => activity.result === null);
  return running ? <p role="status">Running {running.call.name}…</p> : null;
}
```

## builtInMessages [#builtinmessages]

The connect window's copy, one set of [`PickerMessages`](#pickermessages) per locale:

### BuiltInMessages

| Prop | Type             | Description |
| ---- | ---------------- | ----------- |
| `en` | `PickerMessages` |             |
| `fr` | `PickerMessages` |             |
| `he` | `PickerMessages` |             |

Spread one of them to change a few strings:

```tsx
const messages: PickerMessages = { ...builtInMessages.en, connectButton: 'Use my AI', title: 'AI mode' };

export function Root() {
  return (
    <AiConnectProvider client={client} messages={messages}>
      <App />
    </AiConnectProvider>
  );
}
```

## Locale [#locale]

The keys of `builtInMessages`:

### Locales

| Prop | Type   | Description |
| ---- | ------ | ----------- |
| `en` | `"en"` |             |
| `fr` | `"fr"` |             |
| `he` | `"he"` |             |

```ts
const locale: Locale = document.documentElement.lang === 'he' ? 'he' : 'en';
```

## PickerMessages [#pickermessages]

### PickerMessages

| Prop            | Type             | Description |
| --------------- | ---------------- | ----------- |
| `dir`           | `"ltr" \| "rtl"` |             |
| `title`         | `string`         |             |
| `close`         | `string`         |             |
| `back`          | `string`         |             |
| `help`          | `string`         |             |
| `aboutTitle`    | `string`         |             |
| `aboutPoints`   | `string[]`       |             |
| `checking`      | `string`         |             |
| `connectTitle`  | `string`         |             |
| `connectText`   | `string`         |             |
| `connectButton` | `string`         |             |
| `waitingTitle`  | `string`         |             |
| `waitingText`   | `string`         |             |
| `installTitle`  | `string`         |             |
| `installText`   | `string`         |             |
| `installButton` | `string`         |             |
| `declined`      | `string`         |             |
| `tryAgain`      | `string`         |             |

| Key | Where it shows |
| --- | --- |
| `dir` | Reading direction of the window |
| `title` | Window title |
| `close` | Label of the close button |
| `back` | Help link while the help screen is open |
| `help` | Help link, and the help screen's title |
| `aboutTitle` | First line of the help screen |
| `aboutPoints` | List on the help screen |
| `checking` | Step title in `checking` |
| `connectTitle` | Step title in `disconnected` |
| `connectText` | Step text in `disconnected` |
| `connectButton` | Button in `disconnected` |
| `waitingTitle` | Step title in `connecting` |
| `waitingText` | Step text in `connecting` |
| `installTitle` | Step title in `noWallet` |
| `installText` | Step text in `noWallet` |
| `installButton` | Install button in `noWallet`, linking to `client.installUrl` |
| `declined` | Step title in `error` |
| `tryAgain` | Retry button in `error` |

`builtInMessages.en` holds the English text of each key. In the `error` state the step text is the wallet's own message, which is in English.

```ts
const messages: PickerMessages = {
  ...builtInMessages.en,
  title: 'Turn on AI mode',
  connectText: 'Your AI Wallet account answers the questions you ask on this site.',
};
```
