A feature-rich, beautifully crafted React AI chat component library
Kira Chat is a React AI chat component library that provides a ready-to-use chat interface. It supports streaming output, Markdown rendering, math formulas, code highlighting, and tool call visualization. Built on an adapter pattern, you can connect any AI backend by implementing a single function. The built-in CSS Token theme system lets you fully customize the look via variables, with support for light/dark/auto modes.
English | 简体中文
npm install @hakurei/kira-chat
import '@hakurei/kira-chat/styles'
import { AIChat } from '@hakurei/kira-chat'
import type { AIAdapter } from '@hakurei/kira-chat'
const adapter: AIAdapter = async ({ messages, onChunk, onComplete, signal }) => {
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${import.meta.env.VITE_OPENAI_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4o',
stream: true,
messages: messages.map((m) => ({ role: m.role, content: m.content })),
}),
signal,
})
const reader = res.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const text = decoder.decode(value)
for (const line of text.split('\n')) {
if (!line.startsWith('data: ') || line === 'data: [DONE]') continue
try {
const json = JSON.parse(line.slice(6))
const chunk = json.choices?.[0]?.delta?.content
if (chunk) onChunk?.(chunk)
} catch {}
}
}
onComplete?.({ id: '', role: 'assistant', content: '', status: 'done', createdAt: Date.now() })
}
export default function App() {
return (
<AIChat
adapter={adapter}
theme={{ preset: 'light' }}
height="100vh"
/>
)
}
All colors are controlled via CSS custom properties in RGB triplet format (without alpha), designed to work seamlessly with Tailwind's /opacity syntax.
[data-hk-theme="light"] {
--hk-primary: 99 102 241; /* indigo-500 */
--hk-primary-foreground: 255 255 255;
--hk-background: 255 255 255;
--hk-foreground: 15 23 42;
--hk-surface: 248 250 252;
--hk-border: 226 232 240;
/* ... */
}
<AIChat
theme={{
preset: 'light', // 'light' | 'dark' | 'auto'
light: {
primary: '168 85 247', // purple-500
primaryForeground: '255 255 255',
accent: '217 70 239',
radiusLg: '20px', // larger border radius
},
dark: {
primary: '192 132 252', // purple-400
},
}}
/>
| Token | CSS Variable | Description |
|---|---|---|
primary | --hk-primary | Primary color (buttons, links, etc.) |
primaryForeground | --hk-primary-foreground | Primary foreground (white/black) |
background | --hk-background | Root background |
surface | --hk-surface | Card/sidebar background |
surfaceElevated | --hk-surface-elevated | Elevated/highlight background |
foreground | --hk-foreground | Primary text color |
border | --hk-border | Border color |
muted | --hk-muted | Secondary background |
mutedForeground | --hk-muted-foreground | Secondary text color |
destructive | --hk-destructive | Danger/error color |
success | --hk-success | Success color |
radiusSm/Md/Lg/Xl | --hk-radius-* | Border radius sizes |
fontSans/Mono | --hk-font-* | Font stacks |
| Prop | Type | Default | Description |
|---|---|---|---|
adapter | AIAdapter | -- | AI request adapter (required) |
conversations | Conversation[] | -- | Controlled conversation list |
activeConversationId | string | -- | Active conversation ID |
tools | ToolDefinition[] | -- | Tool definitions |
theme | ThemeConfig | { preset: 'light' } | Theme configuration |
showSidebar | boolean | true | Show sidebar |
allowAttachments | boolean | false | Allow file attachments |
placeholder | string | -- | Input placeholder text |
welcomeTitle | string | -- | Welcome page title |
height | string | number | '100%' | Container height |
onConversationCreate | (c: Conversation) => void | -- | Conversation create callback |
onConversationSelect | (id: string) => void | -- | Conversation select callback |
onMessageSend | (msg, cid) => void | -- | Message send callback |
type AIAdapter = (options: SendMessageOptions) => Promise<void>
interface SendMessageOptions {
conversationId: string
messages: Message[] // message history
onChunk?: (chunk: string) => void // streaming text chunk
onToolCall?: (toolCall: ToolCall) => void // tool call
onComplete?: (message: Message) => void // completion
onError?: (error: Error) => void // error
signal?: AbortSignal // abort signal
}
interface ToolDefinition {
name: string
description?: string
parameters?: Record<string, unknown> // JSON Schema
icon?: React.ReactNode // custom icon
renderCall?: (call: ToolCall) => React.ReactNode // custom input renderer
renderResult?: (result: ToolResult, call?: ToolCall) => React.ReactNode // custom result renderer
}
All sub-components can be imported individually:
import {
ChatWindow,
ChatInput,
MessageBubble,
ToolCallCard,
ConversationList,
MarkdownRenderer,
ThemeProvider,
} from '@hakurei/kira-chat'
<ThemeProvider theme={{ preset: 'dark', dark: { primary: '56 189 248' } }}>
<YourApp />
</ThemeProvider>
The component includes a built-in Zustand store, accessible via the useChatStore hook:
import { useChatStore } from '@hakurei/kira-chat'
function MyComponent() {
const {
conversations,
activeConversationId,
createConversation,
deleteConversation,
addMessage,
stopStreaming,
} = useChatStore()
// ...
}
git clone https://github.com/hakurei/kira-chat.git
cd kira-chat
npm install
npm run dev # Start demo playground
Build the library:
npm run build # Output to dist/
Issues and Pull Requests are welcome!
MIT © hakurei