React Components
Streaming-ready UI components for building AI-powered chat interfaces.
npm install @ainative/ai-kit
Requires: React 18+
StreamingMessage
Renders an AI message with real-time streaming animations and Markdown support. Designed to be used with useChat and updated in-place as tokens arrive.
Usage
import { StreamingMessage } from '@ainative/ai-kit';
export function MessageBubble() {
return (
<StreamingMessage
role="assistant"
content="Here is a Python example:\n\n```python\nprint('hello')\n```"
streamingState="streaming"
animationType="smooth"
enableMarkdown={true}
codeTheme="vs-dark"
/>
);
}
Props
| Name | Type | Default | Description |
|---|---|---|---|
role | 'user' | 'assistant' | 'system' | required | Message role — controls layout and visual styling |
content | string | required | Message text. Supports GitHub Flavored Markdown when enableMarkdown is true |
streamingState | 'idle' | 'streaming' | 'done' | 'idle' | Drives animation behavior. Set to 'streaming' while tokens are arriving, then 'done' |
animationType | 'none' | 'fade' | 'typewriter' | 'smooth' | 'typewriter' | How new content is animated in |
enableMarkdown | boolean | true | Render content as GitHub Flavored Markdown |
codeTheme | 'dark' | 'light' | 'vs-dark' | 'github' | 'monokai' | 'nord' | 'dracula' | 'vs-dark' | Syntax highlighting theme applied to fenced code blocks inside the message |
Animation types
| Value | Behavior |
|---|---|
none | Content appears immediately with no animation |
fade | New tokens fade in |
typewriter | Tokens render character by character (classic typewriter effect) |
smooth | Tokens stream in with a smooth sliding transition |
Code theme options
| Value | Description |
|---|---|
dark | Dark background, high contrast |
light | Light background — use on white/grey surfaces |
vs-dark | Visual Studio Code dark (default) |
github | GitHub-style monochrome |
monokai | Monokai classic dark |
nord | Nord arctic color palette |
dracula | Dracula purple-dark theme |
Full chat example
import { useState } from 'react';
import { StreamingMessage, StreamingIndicator } from '@ainative/ai-kit';
import { AINativeProvider, useChat } from '@ainative/react-sdk';
import type { Message } from '@ainative/react-sdk';
function Chat() {
const { messages, sendMessage, isLoading } = useChat({
model: 'meta-llama/llama-3.3-70b-instruct',
temperature: 0.7,
});
const [input, setInput] = useState('');
const handleSubmit = async () => {
if (!input.trim()) return;
const next: Message[] = [
...messages,
{ role: 'user', content: input },
];
setInput('');
await sendMessage(next);
};
return (
<div className="flex flex-col gap-4 p-4">
{messages.map((msg, i) => (
<StreamingMessage
key={i}
role={msg.role}
content={msg.content}
streamingState="done"
animationType="smooth"
codeTheme="vs-dark"
/>
))}
{isLoading && <StreamingIndicator variant="dots" size="md" />}
<div className="flex gap-2">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSubmit()}
placeholder="Type a message…"
className="flex-1 px-3 py-2 rounded border"
/>
<button onClick={handleSubmit} disabled={isLoading}>
Send
</button>
</div>
</div>
);
}
export function App() {
return (
<AINativeProvider config={{ apiKey: process.env.NEXT_PUBLIC_AINATIVE_API_KEY! }}>
<Chat />
</AINativeProvider>
);
}
Accessibility
- Rendered output uses semantic HTML (
<article>,<p>,<code>,<pre>) - Respects
prefers-reduced-motion— animations are disabled for users who opt out - Code blocks include
role="region"and a visually hidden label for screen readers
CodeBlock
Syntax-highlighted code display with an optional copy button and line numbers. Used internally by StreamingMessage for fenced code blocks; also available standalone.
Usage
import { CodeBlock } from '@ainative/ai-kit';
export function Example() {
return (
<CodeBlock
code={`import zerodb from 'zerodb'
const db = await zerodb.connect({ project: 'my-project' })
const results = await db.search('find documents about AI')
console.log(results)`}
language="typescript"
showLineNumbers={true}
showCopyButton={true}
theme="vs-dark"
filename="search.ts"
/>
);
}
Props
| Name | Type | Default | Description |
|---|---|---|---|
code | string | required | Source code string to display |
language | string | 'text' | Language identifier for syntax highlighting (e.g. 'typescript', 'python', 'bash') |
theme | 'dark' | 'light' | 'vs-dark' | 'github' | 'monokai' | 'nord' | 'dracula' | 'vs-dark' | Syntax highlighting theme |
showLineNumbers | boolean | false | Display line numbers in the gutter |
showCopyButton | boolean | true | Render a copy-to-clipboard button in the top-right corner |
filename | string | — | Optional filename shown in a tab above the code block |
Supported languages
Any language identifier supported by the underlying syntax highlighter is accepted. Common values include: typescript, javascript, python, bash, json, yaml, sql, go, rust, html, css, markdown.
Standalone example
import { CodeBlock } from '@ainative/ai-kit';
const snippet = `
from zerodb import ZeroDB
client = ZeroDB(api_key="your-key")
results = client.search(query="semantic search", top_k=5)
`.trim();
export function PythonExample() {
return (
<CodeBlock
code={snippet}
language="python"
theme="monokai"
showLineNumbers={true}
filename="example.py"
/>
);
}
Accessibility
- Code block container has
role="region"with a descriptivearia-label - Copy button includes
aria-label="Copy code"and announces success viaaria-live - Line numbers are
aria-hiddenso screen readers read the code without number noise
StreamingIndicator
Animated loading indicator displayed while an AI response is being generated. Render this alongside your message list whenever isLoading is true.
Usage
import { StreamingIndicator } from '@ainative/ai-kit';
// Minimal usage
<StreamingIndicator />
// With all props
<StreamingIndicator
variant="dots"
size="md"
color="#6366f1"
/>
Props
| Name | Type | Default | Description |
|---|---|---|---|
variant | 'dots' | 'pulse' | 'wave' | 'dots' | Visual animation style |
size | 'sm' | 'md' | 'lg' | 'md' | Size of the indicator |
color | string | — | CSS color value applied to the indicator elements. Falls back to the current text color |
Variant descriptions
| Value | Description |
|---|---|
dots | Three bouncing dots — classic chat "typing" indicator |
pulse | A single element pulsing in and out |
wave | Multiple bars animating up and down in a wave pattern |
Conditional rendering example
import { StreamingMessage, StreamingIndicator } from '@ainative/ai-kit';
import { useChat } from '@ainative/react-sdk';
import type { Message } from '@ainative/react-sdk';
export function ChatFeed() {
const { messages, sendMessage, isLoading } = useChat({
model: 'meta-llama/llama-3.3-70b-instruct',
});
return (
<div>
{messages.map((msg, i) => (
<StreamingMessage
key={i}
role={msg.role}
content={msg.content}
streamingState="done"
/>
))}
{isLoading && (
<div className="flex items-center gap-2 py-2 text-gray-500">
<StreamingIndicator variant="dots" size="sm" />
<span className="text-sm">Thinking…</span>
</div>
)}
</div>
);
}
Accessibility
- The indicator element has
role="status"andaria-label="Loading response" - Animations are suppressed when
prefers-reduced-motion: reduceis set - The component renders nothing when not needed — avoid conditional wrappers that leave an empty
<div>in the DOM
TypeScript types
import type {
// From @ainative/ai-kit
StreamingMessageProps,
CodeBlockProps,
StreamingIndicatorProps,
} from '@ainative/ai-kit';
// Core message and config types from @ainative/react-sdk
import type {
Message,
AINativeConfig,
AINativeError,
ChatCompletionResponse,
CreditBalance,
Usage,
} from '@ainative/react-sdk';
See the source repository for full type definitions.