React Integration
@kaira/chat-react wraps ChatEngine with a provider + focused hooks.
Provider
export interface ChatProviderProps {
readonly engine?: ChatEngine;
readonly createEngine?: () => ChatEngine;
readonly children: ReactNode;
readonly autoConnect?: boolean;
readonly onConnectError?: (error: Error) => void;
}- Pass
enginewhen you already created one. - Pass
createEnginefor lazy initialization. ChatProviderrequires one ofengineorcreateEngine; otherwise it throws.- Set
autoConnectfor mount/unmount lifecycle handling.
Hooks
useMessages(conversationId)-> live + initial message listuseSendMessage()-> stable send callbackuseStreamingMessage(conversationId)-> transient streaming stateuseConnectionState()-> current connection stateuseConversation(conversationId)-> conversation lookupuseOptimisticMessages(messages)-> optimistic reconciliation helpersuseChatEngine()-> direct engine access from context
useStreamingMessage only clears its local preview when the matching
conversation and active stream message finish or error.
Example Chat UI
'use client';
import type { JSX } from 'react';
import {
useMessages,
useOptimisticMessages,
useSendMessage,
useStreamingMessage,
} from '@kaira/chat-react';
import { MessageInput, ThinkingIndicator } from '@kaira/chat-ui';
export function ChatPanel(props: { readonly conversationId: string }): JSX.Element {
const { conversationId } = props;
const messages = useMessages(conversationId);
const { mergedMessages } = useOptimisticMessages(messages);
const sendMessage = useSendMessage();
const streaming = useStreamingMessage(conversationId);
return (
<section>
<div>
{mergedMessages.map((message) => (
<p key={message.id}>
<strong>{message.sender.role}:</strong>{' '}
{message.type === 'text' || message.type === 'ai' ? message.content : message.type}
</p>
))}
</div>
{streaming.isStreaming ? <ThinkingIndicator /> : null}
<MessageInput
onSend={async (text) => {
await sendMessage(conversationId, { type: 'text', content: text });
}}
/>
</section>
);
}