
Security News
CVE Volume Surges Past 48,000 in 2025 as WordPress Plugin Ecosystem Drives Growth
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.
@stream-io/chat-react-ai
Advanced tools
This official repository for Stream Chat's UI components is designed specifically for AI-first applications written in React. When paired with our real-time Chat API, it makes integrating with and rendering responses from LLM providers such as ChatGPT, Gemini, Anthropic or any custom backend easier by providing rich with out-of-the-box components able to render Markdown, Code blocks, tables, streaming messages, file attachments, speech-to-text, etc.
To start, this library includes the following components which assist with this task:
AIMessageComposer - a fully featured message composer with file attachments, speech-to-text, and model selectionAIMarkdown - a markdown renderer optimized for AI-generated content with syntax highlighting and custom tool component supportStreamingMessage - a component that displays text with a typewriter animation effect, ideal for streaming AI responsesSpeechToTextButton - a button component for voice input using the Web Speech APIOur team plans to keep iterating and adding more components over time. If there's a component you use every day in your apps and would like to see added, please open an issue and we will take it into consideration.
The @stream-io/chat-react-ai SDK is available on NPM.
To install it, you may run the following command:
npm install @stream-io/chat-react-ai
# or
pnpm add @stream-io/chat-react-ai
# or
yarn add @stream-io/chat-react-ai
Import the base styles in your application:
import '@stream-io/chat-react-ai/styles/index.css';
All of the components listed below are designed to work seamlessly with our existing React Chat SDK. Our developer guide explains how to get started building AI integrations with Stream.
AIMessageComposerThe AIMessageComposer gives users a complete message composer component with support for text input, file attachments, speech-to-text, and model selection.
import { AIMessageComposer } from '@stream-io/chat-react-ai';
function ChatComposer({ attachments }: ChatComposerProps) {
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
// Handle submission
};
return (
<AIMessageComposer onSubmit={handleSubmit}>
<AIMessageComposer.FileInput name="attachments" />
<AIMessageComposer.TextInput name="message" />
<AIMessageComposer.SpeechToTextButton />
<AIMessageComposer.ModelSelect name="model" />
<AIMessageComposer.SubmitButton />
<AIMessageComposer.AttachmentPreview>
{attachments.map((attachment) => (
<AIMessageComposer.AttachmentPreview.Item {...attachment} />
))}
</AIMessageComposer.AttachmentPreview>
</AIMessageComposer>
);
}
AIMessageComposer.FileInput - File input button for attaching files. Supports multiple file selection.AIMessageComposer.TextInput - Text input field for typing messages. Automatically syncs with composer state.AIMessageComposer.SpeechToTextButton - Button to toggle speech-to-text input using the Web Speech API.AIMessageComposer.SubmitButton - Submit button for sending the message.AIMessageComposer.ModelSelect - Dropdown for selecting AI models. Customizable via options prop.AIMessageComposer.AttachmentPreview - Preview container for attached files.AIMessageComposer.AttachmentPreview.Item - Preview item component, renders a different look for file types that begin with image/.| Name | Type | Required | Description |
|---|---|---|---|
resetAttachmentsOnSelect | boolean | no | Resets file input after selection. Defaults to true. |
nameMapping | { message?: string; attachments?: string } | no | Maps custom input names to internal state. By default, the composer expects inputs named message (for text) and attachments (for files). Use this prop to map different names to these internal keys. |
onSubmit | (e: FormEvent<HTMLFormElement>) => void | no | Form submission handler. |
onChange | (e: FormEvent<HTMLFormElement>) => void | no | Form change handler. |
...HTMLFormElement props | no | Supports all standard HTML form element props. |
[!NOTE] Some default input components come with default
nameattributes (TextInputdefaults to"message",FileInputdefaults to"attachments"). You can override these names via props, and use thenameMappingprop to tell the composer how to map your custom names to its internal state.
AIMarkdownThe AIMarkdown is a markdown renderer optimized for AI-generated content with syntax highlighting and custom tool component support.
| Name | Type | Required | Description |
|---|---|---|---|
children | string | yes | The markdown content to render. |
toolComponents | ToolComponents | no | Custom components for rendering tool outputs (e.g., charts). |
markdownComponents | MarkdownComponents | no | Custom markdown component overrides. |
chartjs - Renders Chart.js visualizations from code blocks with language-chartjs or language-json if it matches supported Chart.js data schema.import { AIMarkdown } from '@stream-io/chat-react-ai';
function MessageContent({ content }) {
return <AIMarkdown>{content}</AIMarkdown>;
}
StreamingMessageThe StreamingMessage is a component that displays text with a typewriter animation effect, similar to ChatGPT. It's ideal for streaming AI responses. It's a simplified wrapper with typewriter animation effect around AIMarkdown component.
| Name | Type | Required | Description |
|---|---|---|---|
text | string | yes | The text content to display with streaming effect. |
import { StreamingMessage } from '@stream-io/chat-react-ai';
function AIResponse({ text }) {
return <StreamingMessage text={text} />;
}
SpeechToTextButtonThe SpeechToTextButton is a button component for voice input using the Web Speech API. It provides a simple interface for converting speech to text with built-in microphone icon and listening state visualization.
| Name | Type | Required | Description |
|---|---|---|---|
options | UseSpeechToTextOptions | no | Options for speech recognition (see useSpeechToText hook documentation for available options like lang, continuous, etc.). |
...HTMLButtonElement props | no | Supports all standard HTML button element props. |
import { SpeechToTextButton } from '@stream-io/chat-react-ai';
function VoiceInputButton() {
return (
<SpeechToTextButton
options={{
lang: 'en-US',
continuous: false,
interimResults: true,
}}
/>
);
}
[!NOTE] When used within an
AIMessageComposer, the button automatically updates the composer's text input. When used standalone, you can control the behavior through thespeechToTextOptions.onTranscriptcallback.
AttachmentPreviewA container component for displaying file attachment previews with support for images and documents.
| Name | Type | Required | Description |
|---|---|---|---|
file | File | yes | The file object to preview. |
state | 'uploading' | 'finished' | 'failed' | 'pending' | no | Upload state indicator. |
title | string | no | Custom title (defaults to filename). |
imagePreviewSource | string | no | Custom image preview URL. |
onDelete | (e: MouseEvent) => void | no | Delete button handler. |
onRetry | (e: MouseEvent) => void | no | Retry button handler (shown on failed state). |
import { AIMessageComposer } from '@stream-io/chat-react-ai';
function CustomAttachmentPreview({
attachments,
}: CustomAttachmentPreviewProps) {
return (
<AIMessageComposer.AttachmentPreview>
{attachments.map((attachment) => (
<AIMessageComposer.AttachmentPreview.Item
key={attachment.id}
file={attachment.file}
onDelete={() => removeAttachment(attachment.id)}
/>
))}
</AIMessageComposer.AttachmentPreview>
);
}
useAttachments (experimental)Manage attachments within the composer context. Must be used within an AIMessageComposer component.
| Name | Type | Description |
|---|---|---|
attachments | Array<{ id: string; file: File; meta?: Record<string, any> }> | Current attachments. |
removeAttachment | (idOrFile: string | File) => void | Remove an attachment. |
updateAttachments | (ids: Array<string | Attachment>, updater: Function) => void | Update attachment metadata. |
import { useAttachments } from '@stream-io/chat-react-ai';
function AttachmentManager() {
const { attachments, removeAttachment, updateAttachments } = useAttachments();
const manageAttachment = (attachmentId: string) => {
// Update attachment metadata
updateAttachments([attachmentId], (attachment) => ({
...attachment,
meta: { uploaded: true },
}));
// Remove an attachment by id or file
removeAttachment(attachmentId);
};
return <div>{attachments.length} files attached</div>;
}
![NOTE] While this attachment API works it's highly recommended to use own attachment API or the one provided by the
stream-chat-react/stream-chat.
useTextAccess and update the text input value. Must be used within an AIMessageComposer component.
| Name | Type | Description |
|---|---|---|
text | string | Current text value. |
setText | (text: string) => void | Update text value. |
import { useText } from '@stream-io/chat-react-ai';
function CustomTextDisplay() {
const { text, setText } = useText();
return (
<div>
<p>Current text: {text}</p>
<button onClick={() => setText('New text')}>Update</button>
</div>
);
}
useMessageTextStreamingCreate a typewriter streaming effect for text content. Useful when you require a custom implementation of a StreamingMessage component.
| Name | Type | Required | Description |
|---|---|---|---|
text | string | yes | The full text to stream. |
streamingLetterIntervalMs | number | no | Interval between character updates. Defaults to 30. |
renderingLetterCount | number | no | Number of characters to render per update. Defaults to 2. |
| Name | Type | Description |
|---|---|---|
streamedMessageText | string | Currently displayed portion of text. |
import { useMessageTextStreaming } from '@stream-io/chat-react-ai';
function StreamedText({ fullText }) {
const { streamedMessageText } = useMessageTextStreaming({
text: fullText,
streamingLetterIntervalMs: 30,
renderingLetterCount: 2,
});
return <div>{streamedMessageText}</div>;
}
useSpeechToTextEnable voice input using the Web Speech API. Provides speech recognition capabilities with real-time transcription.
| Name | Type | Required | Description |
|---|---|---|---|
lang | string | no | Language for speech recognition (e.g., 'en-US', 'es-ES'). Defaults to 'en-US'. |
interimResults | boolean | no | Whether to return interim (partial) results. Defaults to true. |
maxAlternatives | number | no | Maximum number of alternative transcriptions. Defaults to 1. |
continuous | boolean | no | Whether recognition should continue after user stops speaking. Defaults to false. |
onTranscript | (text: string) => void | no | Callback when transcription text changes. |
onError | (error: string) => void | no | Callback when an error occurs. |
| Name | Type | Description |
|---|---|---|
isListening | boolean | Whether speech recognition is currently active. |
isSupported | boolean | Whether the Web Speech API is supported in the browser. |
startListening | () => void | Start speech recognition. |
stopListening | () => void | Stop speech recognition. |
import { useSpeechToText } from '@stream-io/chat-react-ai';
function VoiceInput() {
const [transcript, setTranscript] = useState('');
const { isListening, isSupported, startListening, stopListening } =
useSpeechToText({
lang: 'en-US',
interimResults: true,
onTranscript: (text) => setTranscript(text),
onError: (error) => console.error('Speech recognition error:', error),
});
if (!isSupported) {
return <div>Speech recognition is not supported in your browser</div>;
}
return (
<div>
<button onClick={isListening ? stopListening : startListening}>
{isListening ? 'Stop' : 'Start'} Recording
</button>
<p>Transcript: {transcript}</p>
</div>
);
}
import { AIMessageComposer } from '@stream-io/chat-react-ai';
import '@stream-io/chat-react-ai/styles/index.css';
function ChatInterface() {
const [attachments, setAttachments] = useState([]);
const handleChange = (e) => {
const input = e.currentTarget.elements.namedItem(
'attachments',
) as HTMLInputElement | null;
const files = input?.files ?? null;
if (files) {
// Send to your API
uploadFiles(files).then((uploadedAttachments) =>
setAttachments((currentAttachments) => [
...currentAttachments,
...uploadedAttachments,
]),
);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const message = formData.get('message');
// Send to your API
await sendMessage({ message });
// Reset form
e.target.reset();
};
return (
<AIMessageComposer onChange={handleChange} onSubmit={handleSubmit}>
<AIMessageComposer.AttachmentPreview>
{attachments.map((attachment) => (
<AIMessageComposer.AttachmentPreview.Item {...attachment} />
))}
</AIMessageComposer.AttachmentPreview>
<AIMessageComposer.FileInput name="attachments" />
<AIMessageComposer.TextInput name="message" />
<AIMessageComposer.SpeechToTextButton />
<AIMessageComposer.ModelSelect name="model" />
<AIMessageComposer.SubmitButton />
</AIMessageComposer>
);
}
import { AIMarkdown } from '@stream-io/chat-react-ai';
const customComponents = {
h1: ({ children }) => <h1 className="custom-heading">{children}</h1>,
code: ({ children }) => (
<AIMarkdown.default.code className="custom-class">
{children}
</AIMarkdown.default.code>
),
};
const customToolComponents = {
weather: ({ data, fallback }) => {
try {
const parsedData = JSON.parse(data);
return <div className="weather-tool">{parsedData.result}</div>;
} catch {
return fallback;
}
},
};
function CustomMarkdown({ content }) {
return (
<AIMarkdown
markdownComponents={customComponents}
toolComponents={customToolComponents}
>
{content}
</AIMarkdown>
);
}
import { StreamingMessage } from '@stream-io/chat-react-ai';
function AIResponseStream({ response }) {
return (
<div className="ai-response">
<StreamingMessage text={response} />
</div>
);
}
Stream allows developers to rapidly deploy scalable feeds, chat messaging and video with an industry leading 99.999% uptime SLA guarantee.
Stream provides UI components and state handling that make it easy to build real-time chat and video calling for your app. Stream runs and maintains a global network of edge servers around the world, ensuring optimal latency and reliability regardless of where your users are located.
To learn more about integrating AI and chatbots into your application, we recommend checking out the full list of tutorials across all of our supported frontend SDKs and providers. Stream's Chat SDK is natively supported across:
Stream is free for most side and hobby projects. To qualify, your project/company needs to have < 5 team members and < $10k in monthly revenue. Makers get $100 in monthly credit for video for free. For more details, check out the Maker Account.
We've closed a $38 million Series B funding round in 2021 and we keep actively growing. Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world. Check out our current openings and apply via Stream's website.
Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
Licensed under the Stream License;
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/GetStream/ai-js/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
FAQs
React Chat SDK for AI Components
We found that @stream-io/chat-react-ai demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 8 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.

Security News
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.

Security News
Tailwind Labs laid off 75% of its engineering team after revenue dropped 80%, as LLMs redirect traffic away from documentation where developers discover paid products.