useChat Hook
The useChat hook provides direct access to the chat state and methods. Use this hook in any component within your ChatProvider to interact with the chat functionality.
Overview
The useChat hook returns an object containing the current chat state and methods to manipulate it. It can only be used within a component that is wrapped by ChatProvider.
Basic Usage
import { useChat } from '@cyborg-sdk/react';
function MyChatComponent() {
const { messages, isLoading, error, sendMessage, clearMessages } = useChat();
return (
<div>
{/* Display messages */}
{messages.map(msg => (
<div key={msg.id}>{msg.content}</div>
))}
{/* Show loading state */}
{isLoading && <p>Loading...</p>}
{/* Show errors */}
{error && <p>Error: {error.message}</p>}
{/* Send messages */}
<button onClick={() => sendMessage('Hello!')}>Send</button>
</div>
);
}Return Value
The hook returns an object with the following properties and methods:
interface ChatContextValue {
messages: Message[];
isLoading: boolean;
error: Error | null;
conversationId: string | null;
sendMessage: (content: string) => Promise<void>;
clearMessages: () => void;
}Properties
messages
Array of all chat messages in the conversation.
Type: Message[]
Message structure:
interface Message {
id: string; // Unique message ID
content: string; // Message text content
role: 'user' | 'assistant' | 'system'; // Message sender role
timestamp: Date; // When the message was created
metadata?: Record<string, unknown>; // Optional metadata
}Example:
function ChatHistory() {
const { messages } = useChat();
return (
<div>
{messages.map(message => (
<div key={message.id} className={`msg-${message.role}`}>
{message.content}
<small>{message.timestamp.toLocaleTimeString()}</small>
</div>
))}
</div>
);
}isLoading
Boolean indicating whether a message is currently being sent or received.
Type: boolean
Example:
function ChatInput() {
const { isLoading, sendMessage } = useChat();
return (
<div>
<button disabled={isLoading} onClick={() => sendMessage('Hi')}>
{isLoading ? 'Sending...' : 'Send'}
</button>
</div>
);
}error
Error object if something went wrong, or null if no error.
Type: Error | null
Example:
function ChatStatus() {
const { error } = useChat();
if (error) {
return (
<div className="error-banner">
<strong>Chat Error:</strong> {error.message}
</div>
);
}
return <div>Chat ready</div>;
}conversationId
Unique identifier for the current conversation. Used to maintain conversation history with the backend.
Type: string | null
Example:
function ConversationStatus() {
const { conversationId } = useChat();
return (
<div>
{conversationId && <p>Conversation ID: {conversationId}</p>}
</div>
);
}Methods
sendMessage(content: string): Promise<void>
Send a message to the chat assistant.
Parameters:
content(string, required): The message text to send
Returns: Promise that resolves when the message has been sent
Example:
function ChatInput() {
const [input, setInput] = useState('');
const { sendMessage, isLoading } = useChat();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (input.trim()) {
await sendMessage(input);
setInput(''); // Clear input after sending
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
);
}clearMessages(): void
Clear all messages from the conversation.
Parameters: None
Returns: void
Example:
function ChatControls() {
const { clearMessages } = useChat();
const handleClearChat = () => {
if (window.confirm('Are you sure you want to clear the chat?')) {
clearMessages();
}
};
return (
<button onClick={handleClearChat}>
Clear Chat
</button>
);
}Complete Example: Custom Chat Component
import React, { useState } from 'react';
import { useChat } from '@cyborg-sdk/react';
function CustomChatPanel() {
const [input, setInput] = useState('');
const { messages, isLoading, error, sendMessage, clearMessages } = useChat();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || isLoading) return;
try {
await sendMessage(input);
setInput('');
} catch (err) {
console.error('Failed to send message:', err);
}
};
return (
<div className="chat-panel">
{/* Error Display */}
{error && (
<div className="error-banner">
Error: {error.message}
<button onClick={() => setInput('')}>Dismiss</button>
</div>
)}
{/* Messages */}
<div className="messages">
{messages.length === 0 ? (
<p className="placeholder">Start a conversation...</p>
) : (
messages.map(msg => (
<div
key={msg.id}
className={`message message-${msg.role}`}
>
<p>{msg.content}</p>
<small>
{msg.role === 'user' ? 'You' : 'Assistant'} •
{msg.timestamp.toLocaleTimeString()}
</small>
</div>
))
)}
{isLoading && (
<div className="message message-system">
<p>Typing...</p>
</div>
)}
</div>
{/* Input Form */}
<form onSubmit={handleSubmit} className="input-form">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading || !input.trim()}>
Send
</button>
<button
type="button"
onClick={clearMessages}
disabled={messages.length === 0}
>
Clear
</button>
</form>
</div>
);
}
export default CustomChatPanel;Error Handling
The hook handles errors gracefully. Common scenarios:
function ChatWithErrorHandling() {
const { error, sendMessage } = useChat();
const handleSend = async (message: string) => {
try {
await sendMessage(message);
} catch (err) {
// This is typically caught by the provider
// But you can handle it explicitly if needed
if (err instanceof Error) {
console.error('Chat error:', err.message);
}
}
};
return (
<div>
{error && (
<div className="alert alert-error">
{error.message}
</div>
)}
<button onClick={() => handleSend('Hello')}>
Send
</button>
</div>
);
}TypeScript Support
Full TypeScript support is built-in:
import { useChat, Message } from '@cyborg-sdk/react';
function TypedChatComponent() {
const { messages, sendMessage }: ReturnType<typeof useChat> = useChat();
const renderMessage = (msg: Message) => {
switch (msg.role) {
case 'user':
return <div className="user-msg">{msg.content}</div>;
case 'assistant':
return <div className="ai-msg">{msg.content}</div>;
case 'system':
return <div className="system-msg">{msg.content}</div>;
}
};
return (
<div>
{messages.map(msg => (
<div key={msg.id}>{renderMessage(msg)}</div>
))}
</div>
);
}Common Patterns
Auto-scroll to Latest Message
import { useEffect, useRef } from 'react';
function AutoScrollChat() {
const { messages } = useChat();
const endRef = useRef<HTMLDivElement>(null);
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="messages">
{messages.map(msg => (
<div key={msg.id}>{msg.content}</div>
))}
<div ref={endRef} />
</div>
);
}Disable Input While Loading
function ChatInput() {
const { sendMessage, isLoading } = useChat();
const [input, setInput] = useState('');
return (
<input
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
placeholder={isLoading ? 'Waiting for response...' : 'Type here...'}
/>
);
}Context Requirement
The hook must be used within a component wrapped by ChatProvider. Using it outside will throw an error:
import { useChat } from '@cyborg-sdk/react';
// ❌ This will fail - not inside ChatProvider
function InvalidUsage() {
const chat = useChat(); // Error: useChat must be used within a ChatProvider
return null;
}
// ✅ This works - inside ChatProvider
function App() {
return (
<ChatProvider config={{ apiKey: 'YOUR_API_KEY' }}>
<ValidUsage />
</ChatProvider>
);
}
function ValidUsage() {
const { messages } = useChat(); // ✅ Works
return <div>{messages.length} messages</div>;
}