# Quickstart

> **Info:** This is documentation for **Stream Chat React Native SDK v9**, which is no longer actively maintained. For up-to-date documentation, see the [latest version (v10)](https://getstream.io/chat/docs/sdk/react-native/).

The AI components give your React Native app the screens of an AI assistant: a conversation that streams the agent's replies, a prompt composer with photos and dictation, the agent's reasoning and tool calls, and a face for a voice agent. They ship with `stream-chat-react-native` and `stream-chat-expo` in their own entry points: `stream-chat-react-native/ai-components`, or `stream-chat-expo/ai-components` with Expo. The agent behind them runs on [Stream Agents](https://getstream.io/agents/docs/quickstart/?client=javascript): you describe it once, with its instructions, knowledge, tools and voice, and your app talks to it with the `@stream-io/stream-agents` SDK.

This page shows what you can build, how the pieces fit together, and takes you from an empty project to a working assistant.

## What you can build

| You want to build                                                                               | Start with                                                                                                                    |
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| An AI-first app, like ChatGPT or Claude: one user, one assistant, many conversations            | This page, then the [Personal assistant](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/personal-assistant/) use case |
| A support agent that answers from your help center, looks up orders and hands over to your team | The [Customer support agent](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/customer-support-agent/) use case         |
| A voice agent the user talks to, with a visual presence                                         | The [Voice agent](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/voice-agent/) use case                               |
| An agent that shows its work and asks before it acts                                            | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/)                            |
| An agent that uses the phone: the clock, the location, a screen of your app                     | The [Personal assistant](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/personal-assistant/) use case                 |
| Replies that are UI, such as a trip plan with a map and a form                                  | [Generative UI](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/generative-ui/)                                                  |
| AI replies in a chat you already built with `MessageList`                                       | [SDK integration](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/sdk-integration/)                                              |

Voice runs through all of them. The composer has a microphone button that dictates into it, and the same agent can hold the conversation out loud: dictation listens, the agent answers, the phone reads the answer aloud, and `VoiceGlow` or `VoiceWave` shows what is happening.

## How it fits together

Your app never talks to the model, and it doesn't run the agent. It opens a **session** with your agent and asks it things. The agent runs on Stream's router, which calls the model, runs the agent's tools and writes the conversation into a Stream Chat channel as it goes. The components show that channel:

```mermaid
sequenceDiagram
    participant App as React Native app
    participant Router as Stream Agents
    participant LLM as Model
    participant Chat as Stream Chat
    App->>Router: sessions.create() with @stream-io/stream-agents
    App->>Router: responses.create("How do I...?")
    Router->>LLM: The conversation so far
    LLM-->>Router: Reasoning, tool calls, tokens
    Router-->>Chat: The question, and the reply as it streams, with its steps
    Chat-->>App: The channel's messages
    Note over App: AIConversation shows them
```

- **`@stream-io/stream-agents`** signs the user in, opens sessions, asks, stops a reply, answers the questions of tools that ask first, and runs the tools your app gives the agent on the phone.
- **`stream-chat-react-native/ai-components`** shows it all: `AIConversation` and `ComposerView`, the reasoning and tool calls of each reply, the UI the agent writes, and the voice visualizations. `AIConversationController` keeps the conversation's state between them and your agent.
- **Your agent** lives in a folder of plain files that you sync with the Stream CLI: its instructions, knowledge, skills, tools, connectors and models.

## Before you start

- A Stream app, from the [Stream Dashboard](https://getstream.io/signup/?product=chat).
- The beta [Stream CLI](https://getstream.io/cli/docs/install/), which has the `getstream agents` commands.
- A React Native 0.76 or later app, with the [Chat SDK's requirements](https://getstream.io/chat/docs/sdk/react-native/v9/basics/installation/) installed. With Expo, the app runs in a development build: Expo Go doesn't have the native code the SDK and `react-native-keyboard-controller` need.

## Step 1: Create the agent

Install the beta CLI, pick your Stream app, and make an agent from the text template:

```sh
curl -fsSL https://getstream.io/cli/beta/install.sh | bash
getstream init                                     # pick or create a Stream app
getstream agents init my-agent --template text     # or --template voice
```

That creates `agents/my-agent/`, with the agent's settings in `agent.yaml`, its system prompt in `instructions.md`, and a guardrail in `guardrail.md`. The template's guardrail turns away anything that isn't about Acme Corp, its example company, so write what the agent is for in both files, then sync it to Stream and try it in the terminal:

```sh
getstream agents sync my-agent
getstream agents test my-agent
```

The [Stream Agents quickstart](https://getstream.io/agents/docs/quickstart/?client=javascript) covers the agent's folder: knowledge, skills, tools, connectors and guardrails.

## Step 2: Install the SDKs

Add `@stream-io/stream-agents`, the client for your agent, and the Chat SDK, which has the views. `stream-chat` is the Stream Chat client that reads the conversation the agent writes:

```bash
# React Native CLI
yarn add @stream-io/stream-agents stream-chat stream-chat-react-native

# Expo
npx expo install @stream-io/stream-agents stream-chat stream-chat-expo
```

The AI components are available from version 9.10.0 of `stream-chat-react-native` and `stream-chat-expo`, `VoiceGlow` and `VoiceWave` from 9.11.0, and the reply's steps and generative UI from 9.13.0, and `AIConversation`'s entry point and the conversation controller from 9.14.0, so this page needs 9.14.0 or later. Their JavaScript dependencies are installed with the SDK, so there's no separate package to add. Import them from the AI entry points:

```tsx
// React Native CLI
import { ComposerView } from "stream-chat-react-native/ai-components";
import { AIConversation } from "stream-chat-react-native/ai-components/conversation";

// Expo
import { ComposerView } from "stream-chat-expo/ai-components";
import { AIConversation } from "stream-chat-expo/ai-components/conversation";
```

The examples on these pages import from `stream-chat-react-native`. With Expo, use `stream-chat-expo` instead. Apps that never import the AI entry points don't bundle any of them, and apps that only use the AI components don't bundle the rest of the chat SDK.

`AIConversation` has an entry point of its own, because it needs `react-native-keyboard-controller` 1.21.7 or later, which keeps the user's question in place and makes room for the keyboard below it. Install it, and wrap your app in its `KeyboardProvider`:

```bash
yarn add react-native-keyboard-controller
# With Expo: npx expo install react-native-keyboard-controller
```

Charts and the voice visualizations draw with Skia, which needs two native libraries that only apps using them have to install. `react-native-reanimated`, `react-native-gesture-handler` and `react-native-svg` are already required by the SDK:

```bash
yarn add victory-native @shopify/react-native-skia
# With Expo: npx expo install victory-native @shopify/react-native-skia
```

The components depend on `zod`, which uses `export * as` syntax that the React Native Babel preset doesn't transform. Install the `export-namespace-from` plugin, and add it to `babel.config.js`:

```bash
yarn add -D @babel/plugin-transform-export-namespace-from
```

```js
module.exports = {
  presets: ["module:@react-native/babel-preset"],
  plugins: [
    // ... rest of your @babel plugins
    "@babel/plugin-transform-export-namespace-from",
    // react-native-worklets/plugin has to be the last one
    "react-native-worklets/plugin",
  ],
};
```

Hermes has no `crypto.randomUUID`, which `@stream-io/stream-agents` uses for the ids of its requests. Add one where your app starts, before anything else imports the client:

```ts title="polyfills.ts"
type CryptoLike = { randomUUID?: () => string };
const holder = globalThis as { crypto?: CryptoLike };

if (typeof holder.crypto?.randomUUID !== "function") {
  holder.crypto = Object.assign(holder.crypto ?? {}, {
    randomUUID: () =>
      "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
        const random = Math.floor(Math.random() * 16);
        // The bits that mark a version 4 UUID.
        return (char === "x" ? random : (random & 0x3) | 0x8).toString(16);
      }),
  });
}
```

The composer's microphone button needs setup in the native project: [Dictation](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#dictation) describes the Podfile line for iOS, the permission for Android and the config plugin for Expo. If your agent answers with [generative UI](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/generative-ui/), its A2UI renderer comes with the SDK, in `stream-chat-react-native/a2ui`.

Is your agent on a backend of your own? Skip `@stream-io/stream-agents`, and have your backend write the agent's replies into a Stream Chat channel, by hand or with the [Stream Chat AI SDK](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/stream-chat-ai-sdk/) or the [Stream Chat LangChain SDK](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/stream-chat-langchain-sdk/). [Conversation state](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-state/) describes what it sends: the `ai_generated` and `generating` fields of a reply, and the AI indicator events that say what the agent is doing. A transport of your own then connects the conversation to it, as described there.

### Optional libraries

Two optional libraries add features. The SDK finds whichever you install:

- **Photos.** The composer's sheet picks photos from the library and takes them with the camera when `react-native-image-picker` (React Native CLI) or `expo-image-picker` (Expo) is installed. [Attach photos](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#attach-photos) lists their permissions.
- **Clipboard.** Code blocks and replies get a copy button when `@react-native-clipboard/clipboard` (React Native CLI) or `expo-clipboard` (Expo) is installed.

## Step 3: Sign the user in

Create one `Client` for your app with your API key, and set the user once they sign in. The user's Stream token comes from your backend, which mints it with your API secret, as described in [authentication](https://getstream.io/docs/platform/authentication/). The client asks for it again when it expires. The same user connects to Stream Chat, and `api.use` hands that client to the agent client, so the agent's sessions read the conversation on the connection your app already has, rather than on a second one:

```ts title="agent.ts"
import "./polyfills";

import { Client } from "@stream-io/stream-agents";
// Lets session.chat() open the conversation's channel on your Stream Chat client. Import it once.
import "@stream-io/stream-agents/chat";
import { StreamChat } from "stream-chat";

const API_KEY = "your_api_key";

export const api = new Client({ apiKey: API_KEY });
export const chatClient = StreamChat.getInstance(API_KEY);

export const fetchStreamToken = () =>
  fetch("https://your-backend.example.com/stream-token").then((response) =>
    response.text(),
  );

export async function signIn(user: { id: string; name: string }) {
  await api.setUser(user, fetchStreamToken);
  await chatClient.connectUser(user, fetchStreamToken);
  api.use({ chat: chatClient });
}
```

If your app already has a connected `StreamChat` client, such as the one `Chat` takes, pass it to `api.use` and skip the last two lines of `signIn`.

To try things out before you have a backend, mint a token for a test user with the CLI, `getstream token jane --ttl 2h`, and pass it as `api.setUser({ id: "jane" }, "...")` and `chatClient.connectUser({ id: "jane" }, "...")`.

## Step 4: Connect a conversation to the agent

A session is one conversation with your agent, and its channel is where the agent writes it: the user's questions, each reply as it streams, and the reasoning and tool calls of each reply as the reply's attachments. Add a function to `agent.ts` that opens a session and its channel:

```ts title="agent.ts"
import type { Session } from "@stream-io/stream-agents";
import type { Channel } from "stream-chat";

export type AgentChat = {
  session: Session;
  channel: Channel;
};

/** Opens a conversation with the agent, or the channel of one already open. */
export async function openConversation(session?: Session): Promise<AgentChat> {
  const opened = session ?? (await api.agent("my-agent").sessions.create());
  // The channel the agent writes the conversation into.
  const { channel } = await opened.chat();
  const agentChannel = channel as Channel;
  await agentChannel.watch();
  return { session: opened, channel: agentChannel };
}
```

The views show `AIMessage` values, and keep them in an `AIConversationController`. The controller reaches your agent through a **transport**, which you write: it follows the channel's messages, tells the controller what the agent is doing, and asks and stops through the session. `createFromStreamMessage` turns each of the channel's messages into an `AIMessage`: a reply is a message with `ai_generated: true`, it's being written while `generating` is `true`, and its steps are its `ai_reasoning` and `ai_tool_call` attachments. The channel's state lists its messages oldest first, which is the order the conversation wants:

```ts title="agentTransport.ts"
import type { ConversationSnapshot, Session } from "@stream-io/stream-agents";
import type { Channel } from "stream-chat";
import {
  type AIAgentActivity,
  AIAgentActivityStates,
  type AIConversationTransport,
  createFromStreamMessage,
} from "stream-chat-react-native/ai-components";

// What the agent says it's doing, from the session's socket.
const activityOf = (
  snapshot: ConversationSnapshot,
): AIAgentActivity | undefined => {
  if (snapshot.kind === "responding") {
    return { state: AIAgentActivityStates.thinking };
  }
  if (snapshot.kind === "working") {
    return {
      state: AIAgentActivityStates.checkingExternalSources,
      text: `Working on ${snapshot.skills.map((skill) => skill.replace(/_/g, " ")).join(" and ")}`,
    };
  }
  return undefined;
};

export const createAgentTransport = (
  session: Session,
  channel: Channel,
): AIConversationTransport => ({
  connect: (feed) => {
    // Gives back the same AIMessage for a message that didn't change,
    // so only the messages an update touched render again.
    const fromStreamMessage = createFromStreamMessage();
    const updateMessages = () =>
      feed.updateMessages(channel.state.messages.map(fromStreamMessage));
    updateMessages();
    // Every event of the channel, so new and updated messages show.
    const subscription = channel.on(updateMessages);

    const updateActivity = () =>
      feed.updateActivity(activityOf(session.state.get()));
    updateActivity();
    const stopFollowing = session.state.subscribe(updateActivity);

    return () => {
      subscription.unsubscribe();
      stopFollowing();
    };
  },
  // The router writes the question into the channel itself.
  send: async ({ text }) => {
    await session.responses.create(text);
  },
  stop: async () => {
    session.interrupt();
  },
});
```

[Conversation state](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-state/) covers the transport and the controller in full: attachments, errors and a retry, ratings, and loading older messages.

## Step 5: Show the conversation

Show the conversation with `AIConversation`, and the composer below it. Both take the controller as their `conversation`: the composer sends to it, and shows a stop button while the agent answers. The `approver` answers the tools that ask before they run. The router has no regenerate or edit of its own, so the controller leaves those out:

```tsx title="AgentConversation.tsx"
import { useEffect, useState } from "react";
import { StyleSheet, View } from "react-native";
import {
  AIClientIdentity,
  AIConversationController,
  ComposerView,
} from "stream-chat-react-native/ai-components";
import { AIConversation } from "stream-chat-react-native/ai-components/conversation";

import { type AgentChat, chatClient } from "./agent";
import { createAgentTransport } from "./agentTransport";

export const AgentConversation = ({ chat }: { chat: AgentChat }) => {
  const { channel, session } = chat;
  // One controller per conversation.
  const [conversation] = useState(
    () =>
      new AIConversationController({
        actions: { edit: false, regenerate: false },
        approver: {
          clientId: AIClientIdentity.installId(),
          decide: async (call, allowed) => session.decide(call.id, { allowed }),
          userId: chatClient.userID ?? "",
        },
        transport: createAgentTransport(session, channel),
      }),
  );
  useEffect(() => () => conversation.dispose(), [conversation]);

  return (
    <View style={styles.container}>
      {/* The conversation scrolls inside itself and makes room for the keyboard. */}
      <AIConversation conversation={conversation} />
      <ComposerView conversation={conversation} />
    </View>
  );
};

const styles = StyleSheet.create({ container: { flex: 1 } });
```

`AIConversation` fills the room its parent gives it and scrolls inside itself, so give the parent a height, here with `flex: 1`. The keyboard is the SDK's: with `KeyboardProvider` above it, the conversation makes room for the keyboard and the composer rides it, so don't wrap them in a `KeyboardAvoidingView`. Make one controller per conversation and dispose of it once it's gone. To switch to another conversation, render the screen again with a `key`.

The screen renders inside `StreamTheme`, which gives the AI components their colors, as [Theming](#theming) describes. Sign the user in, then open a conversation, for example from a button:

```tsx title="App.tsx"
import { useState } from "react";
import { Pressable, Text } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { KeyboardProvider } from "react-native-keyboard-controller";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { StreamTheme } from "stream-chat-react-native/ai-components";

import { type AgentChat, openConversation, signIn } from "./agent";
import { AgentConversation } from "./AgentConversation";

export default function App() {
  const [chat, setChat] = useState<AgentChat>();

  const start = async () => {
    await signIn({ id: "jane", name: "Jane" });
    setChat(await openConversation());
  };

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <SafeAreaProvider>
        <KeyboardProvider>
          <StreamTheme>
            {chat ? (
              <AgentConversation chat={chat} />
            ) : (
              <Pressable onPress={() => void start()}>
                <Text>Ask the agent</Text>
              </Pressable>
            )}
          </StreamTheme>
        </KeyboardProvider>
      </SafeAreaProvider>
    </GestureHandlerRootView>
  );
}
```

Run the app and ask something. While the agent works on a reply that shows nothing yet, the conversation shows it thinking. Then the reply writes itself out, with Markdown, code and charts, and with the reasoning and tool calls that led to it. While it writes, the send button is a stop button, and under each finished reply the user can copy and share it.

The [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/) guide covers what else the screen can show, such as suggestions while it's empty, older messages, and an error with a way to try again. The [Composer](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/) guide adds photos and chat options.

## Step 6: Add voice

The composer already takes voice: its microphone button dictates into it with the platform's speech recognizer, once [dictation is set up](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#dictation).

To let the user talk to the agent instead, build a voice mode on the same conversation: dictation listens, the controller sends what it heard, and when the reply is done the phone reads it aloud. Give the agent a face with `VoiceGlow`, which moves differently while the agent is connecting, listening, thinking and speaking:

```tsx
import { VoiceGlow } from "stream-chat-react-native/ai-components";

<VoiceGlow state="thinking" style={{ width: 240, height: 240 }} />;
```

The [Voice agent](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/voice-agent/) use case builds the whole screen: the agent's state, the visualizations, and a hands-free conversation. An agent made with `--template voice` also lets you pick its speech-to-text and text-to-speech models, its voice and its greeting.

## Theming

The AI components take their colors, fonts and sizes from an AI theme of their own, defined in [`ai-components/theme.ts`](https://github.com/GetStream/stream-chat-react-native/blob/v9-release/package/src/ai-components/theme.ts), which `StreamTheme` provides. Place it high in your component tree, and pass a `style` to change the theme. It's deeply merged over the defaults, so pass only what you change:

```tsx
<StreamTheme
  style={{
    colors: { accent_blue: "#7c3aed" },
    composer: { inputPill: { borderRadius: 12 } },
    message: { userMessageBackground: "#ede9fe" },
  }}
>
  {children}
</StreamTheme>
```

| Key                                      | Styles                                                                               | Guide                                                                                                                |
| ---------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| `colors`                                 | The palette the rest of the theme follows                                            | [Dark mode](#dark-mode)                                                                                              |
| `markdown`, `aiTypingIndicatorView`      | Markdown, code and the thinking indicator                                            | [Streaming messages](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/streaming-messages/#styling-and-texts)             |
| `composer`, `sheet`, `chatOptions`       | The composer, its sheet, and the chat options and their chip                         | [Composer](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#styling-and-texts)                                 |
| `conversation`, `message`, `suggestions` | The list, the user's and the agent's messages and their actions, and the suggestions | [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/#styling-and-texts)               |
| `messageParts`                           | Reasoning, tool calls and their questions                                            | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/#styling-and-texts) |
| `generatedUI`, `a2ui`                    | The UI an agent writes for a reply                                                   | [Generative UI](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/generative-ui/#theming)                                 |
| `voiceVisualization`                     | `VoiceGlow` and `VoiceWave`                                                          | [Voice visualizations](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/voice-visualizations/#colors)                    |
| `icons`                                  | Any icon the messages and steps draw, by name, over `defaultAIIcons`                 | [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/#styling-and-texts)               |

### Dark mode

The theme has no dark mode of its own: `StreamTheme` doesn't follow the system color scheme, so pass the colors of the scheme you want to show:

```tsx
import { useColorScheme } from "react-native";

const darkColors = {
  accent_blue: "#4C9DFF",
  accent_red: "#FF636E",
  black: "#FFFFFF",
  code_block: "#1E1E22",
  grey: "#A1A1AA",
  grey_neutral: "#C5C5C8",
  grey_dark: "#71717A",
  grey_gainsboro: "#3F3F46",
  grey_whisper: "#27272F",
  overlay: "#000000CC",
  transparent: "transparent",
  white: "#050509",
  white_smoke: "#121214",
  shimmer: "#FFFFFF",
};

const Theme = ({ children }: { children: React.ReactNode }) => {
  const scheme = useColorScheme();
  return (
    <StreamTheme style={{ colors: scheme === "dark" ? darkColors : {} }}>
      {children}
    </StreamTheme>
  );
};
```

### Localization

Every text the components show, such as the composer's placeholder or "Thought for 12s", goes through the Chat SDK's translations. Inside `Chat`, they follow the active language, and you change or translate them with `Streami18n`, as [Localization](https://getstream.io/chat/docs/sdk/react-native/v9/guides/localization/) describes. The keys are the English texts. Outside `Chat`, they're in English, and the `TranslationProvider` that the AI entry point exports gives them a translator without it.

## The components

Everything below is exported from `stream-chat-react-native/ai-components`, except `AIConversation`, which comes from `ai-components/conversation`, and the A2UI renderer, from `stream-chat-react-native/a2ui`.

| Component or hook                                                                                                               | What it does                                                                                               | Guide                                                                                                             |
| ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `AIConversation`                                                                                                                | Lays out a conversation the way AI assistants do                                                           | [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/)                              |
| `AIUserMessage`, `AIAssistantMessage`, `AIReplyActions`, `AIMessageAttachment`                                                  | Show the user's messages and the agent's replies, their images and files, and the actions under each reply | [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/)                              |
| `AISuggestions`                                                                                                                 | Offers conversation starters                                                                               | [Conversation view](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-view/#suggestions-to-start-with)    |
| `AIConversationController`, `useAIConversationState`                                                                            | Keep a conversation's state, and reach your agent through a transport                                      | [Conversation state](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-state/)                            |
| `fromStreamMessage`, `createFromStreamMessage`                                                                                  | Turn a Stream Chat message into the `AIMessage` the views show                                             | [Conversation state](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/conversation-state/)                            |
| `ComposerView`, `ChatOptionsList`, `SelectedChatOptionChip`, `useComposerText`, `useComposerChatOption`, `useComposerIsEditing` | Takes what the user writes, with photos, chat options and dictation                                        | [Composer](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/)                                                |
| `SpeechToTextButton`, `useDictation`, `isDictationAvailable`                                                                    | Dictate with the platform's speech recognition                                                             | [Composer](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#dictation)                                      |
| `StreamingMessageView`, `Markdown`                                                                                              | Types out Markdown, code, tables and charts as they stream                                                 | [Streaming messages](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/streaming-messages/)                            |
| `AITypingIndicatorView`                                                                                                         | Shows what the agent is doing before its reply has text                                                    | [Streaming messages](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/streaming-messages/)                            |
| `AIMessageParts`, `AIMessagePart`, `AIReasoning`, `AIThinking`, `AIToolCall`                                                    | Shows a reply's reasoning and tool calls                                                                   | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/)                |
| `AIToolApproval`, `AIToolApprovalCard`, `AIToolApproverProvider`, `AIClientIdentity`                                            | Asks the user before a tool runs                                                                           | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/#tool-approvals) |
| `AIToolAuthorization`, `AIToolAuthorizationCard`, `createAuthSessionAuthorizer`                                                 | Signs the user in to a connector a tool call waits for                                                     | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/#tool-sign-ins)  |
| `AIClientToolRunner`                                                                                                            | Runs the tools the agent asks the phone to run                                                             | [Reasoning and tool calls](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/reasoning-and-tool-calls/#client-tools)   |
| `AIUIHost`, `AIUIView`, `defineAIUIComponent`, `AIUIEntrance`, `createA2UIRenderer`                                             | Shows UI the agent writes in place of a reply's text                                                       | [Generative UI](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/generative-ui/)                                      |
| `VoiceGlow`, `VoiceWave`, `useDictationAudioLevel`                                                                              | Shows what a voice agent is doing                                                                          | [Voice visualizations](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/voice-visualizations/)                        |
| `StreamTheme`, `useTheme`                                                                                                       | Set the colors, styles and icons of all of them                                                            | [Theming](#theming)                                                                                               |

The main `stream-chat-react-native` entry point has AI support of its own, for chats built with `MessageList`: its own `StreamingMessageView` and `AITypingIndicatorView`, a stop button in the default composer, and the `useAIState` and `useStreamingMessage` hooks, described in [SDK integration](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/sdk-integration/) and the [AI hooks](https://getstream.io/chat/docs/sdk/react-native/v9/ui-components/ai/hooks/).

## Migrating from `@stream-io/chat-react-native-ai`

The components used to ship as the standalone `@stream-io/chat-react-native-ai` package, which is now deprecated. Their API is unchanged. To migrate:

1. Uninstall `@stream-io/chat-react-native-ai` and import from the new entry point:

   ```diff
   - import { ComposerView, StreamingMessageView } from "@stream-io/chat-react-native-ai";
   + import { ComposerView, StreamingMessageView } from "stream-chat-react-native/ai-components";
   ```

   With Expo, import from `stream-chat-expo/ai-components`.

2. Enable speech-to-text on iOS, as described in [Dictation](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/composer/#dictation). With Expo, replace the `@stream-io/chat-react-native-ai` config plugin with `stream-chat-expo`.
3. Keep the `export-namespace-from` Babel plugin. The `@babel/plugin-proposal-export-namespace-from` package you may already have works too.
4. `ShimmeringView` and `toHexFromRNColor` were removed. `AITypingIndicatorView` now uses the SDK's native shimmer, so `@shopify/react-native-skia` is only needed for charts and the voice visualizations.
5. `SpeechToTextButton` and `isDictationAvailable` are now exported.

## Next steps

- [Customer support agent](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/customer-support-agent/): answers from your help center, order lookups, a refund the user confirms, and a handover to your team.
- [Personal assistant](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/personal-assistant/): a list of conversations, tools that run on the phone, photos, and a voice mode.
- [Voice agent](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/use-cases/voice-agent/): a hands-free voice conversation with the agent as a glowing globe.
- The complete assistants in the [`ExpoChatGPTSample`](https://github.com/GetStream/stream-chat-react-native/tree/v9-release/examples/ExpoChatGPTSample) and [`ExpoCrewAssistant`](https://github.com/GetStream/stream-chat-react-native/tree/v9-release/examples/ExpoCrewAssistant) folders of the SDK repository.
- [Stream Agents](https://getstream.io/agents/docs/quickstart/?client=javascript): the agent itself, its tools, knowledge, connectors and phone numbers.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/](https://getstream.io/chat/docs/sdk/react-native/v9/ai-components/).