Skip to main content

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

NameTypeDefaultDescription
role'user' | 'assistant' | 'system'requiredMessage role — controls layout and visual styling
contentstringrequiredMessage 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
enableMarkdownbooleantrueRender 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

ValueBehavior
noneContent appears immediately with no animation
fadeNew tokens fade in
typewriterTokens render character by character (classic typewriter effect)
smoothTokens stream in with a smooth sliding transition

Code theme options

ValueDescription
darkDark background, high contrast
lightLight background — use on white/grey surfaces
vs-darkVisual Studio Code dark (default)
githubGitHub-style monochrome
monokaiMonokai classic dark
nordNord arctic color palette
draculaDracula 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

NameTypeDefaultDescription
codestringrequiredSource code string to display
languagestring'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
showLineNumbersbooleanfalseDisplay line numbers in the gutter
showCopyButtonbooleantrueRender a copy-to-clipboard button in the top-right corner
filenamestringOptional 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 descriptive aria-label
  • Copy button includes aria-label="Copy code" and announces success via aria-live
  • Line numbers are aria-hidden so 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

NameTypeDefaultDescription
variant'dots' | 'pulse' | 'wave''dots'Visual animation style
size'sm' | 'md' | 'lg''md'Size of the indicator
colorstringCSS color value applied to the indicator elements. Falls back to the current text color

Variant descriptions

ValueDescription
dotsThree bouncing dots — classic chat "typing" indicator
pulseA single element pulsing in and out
waveMultiple 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" and aria-label="Loading response"
  • Animations are suppressed when prefers-reduced-motion: reduce is 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.