---
title: "AI Kit React Components"
description: "Reference for StreamingMessage, CodeBlock, and StreamingIndicator — the UI components in @ainative/ai-kit"
canonical: "https://docs.ainative.studio/docs/ai-kit/components"
last-updated: "2026-10-26T19:08:16.785Z"
---

# AI Kit React Components

Source: https://docs.ainative.studio/docs/ai-kit/components

> Reference for StreamingMessage, CodeBlock, and StreamingIndicator — the UI components in @ainative/ai-kit

# React Components

Streaming-ready UI components for building AI-powered chat interfaces.

```bash
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

```tsx
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

```tsx
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

```tsx
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

```tsx
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

```tsx
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

```tsx
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

```typescript
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.
