useChat Hook
The useChat hook provides direct access to the chat state and methods. Use this hook in any component within your CyborgProvider 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 CyborgProvider.
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 CyborgChatContextValue {
// State
messages: Message[];
isLoading: boolean;
error: Error | null;
conversationId: string | null;
isOpen: boolean;
hasUniqueIdentifier: boolean;
isLoadingConversation: boolean;
// Methods
sendMessage: (content: string) => Promise<void>;
clearMessages: () => void;
startNewConversation: () => void;
loadConversation: (conversationId: string) => Promise<void>;
listConversations: () => Promise<ConversationSummary[]>;
openChat: () => void;
closeChat: () => void;
triggerPrompt: (prompt: string, options?: TriggerPromptOptions) => Promise<void>;
}Properties
messages
Array of all chat messages in the conversation.
Type: Message[]
Message structure:
interface Message {
id: string;
content: string;
role: 'user' | 'assistant' | 'system' | 'tool';
timestamp: Date;
toolCallId?: string;
metadata?: Record<string, unknown>;
}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
error
Error object if something went wrong, or null if no error.
Type: Error | null
conversationId
Unique identifier for the current conversation.
Type: string | null
isOpen
Whether the chat modal is currently open.
Type: boolean
hasUniqueIdentifier
Whether uniqueIdentifier is configured (enables server-side conversation history).
Type: boolean
isLoadingConversation
Loading state specifically for conversation operations (load/list).
Type: boolean
Methods
sendMessage(content: string): Promise<void>
Send a message to the chat assistant.
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('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>Send</button>
</form>
);
}clearMessages(): void
Clear all messages from the conversation.
const { clearMessages } = useChat();
<button onClick={clearMessages}>Clear Chat</button>startNewConversation(): void
Start a new conversation (clears messages and resets conversationId locally).
const { startNewConversation } = useChat();
<button onClick={startNewConversation}>New Chat</button>openChat(): void
Open the chat widget.
const { openChat } = useChat();
<button onClick={openChat}>Open Chat</button>closeChat(): void
Close the chat widget.
const { closeChat } = useChat();
<button onClick={closeChat}>Close Chat</button>triggerPrompt(prompt: string, options?: TriggerPromptOptions): Promise<void>
Open the chat with a pre-filled prompt, optionally starting a new conversation.
const { triggerPrompt } = useChat();
// Basic usage
triggerPrompt('How do I reset my password?');
// With options
triggerPrompt('Help me upgrade my plan', {
startNew: true, // Start a new conversation (default: true)
instruction: 'Focus on pricing and plan differences'
});Options:
interface TriggerPromptOptions {
startNew?: boolean; // Whether to start a new conversation. Default: true
instruction?: string; // Additional instruction for this prompt
}Example: Help Buttons
function HelpButton({ topic }) {
const { triggerPrompt } = useChat();
const handleHelp = () => {
triggerPrompt(`Help me understand ${topic}`, {
startNew: true,
instruction: `Focus on explaining ${topic} clearly`
});
};
return (
<button onClick={handleHelp}>
Get help with {topic}
</button>
);
}loadConversation(conversationId: string): Promise<void>
Load a previous conversation from the backend. Requires uniqueIdentifier to be set.
const { loadConversation } = useChat();
await loadConversation('conv_abc123');listConversations(): Promise<ConversationSummary[]>
List all conversations for the current user. Requires uniqueIdentifier to be set.
const { listConversations, loadConversation } = useChat();
const conversations = await listConversations();
// Returns: [{ id, title, createdAt, updatedAt }]
// Load a specific conversation
await loadConversation(conversations[0].id);ConversationSummary type:
interface ConversationSummary {
id: string;
title?: string;
createdAt: Date;
updatedAt: Date;
}Conversation History
When uniqueIdentifier is set in config, conversation history is enabled:
function ConversationList() {
const { listConversations, loadConversation, hasUniqueIdentifier } = useChat();
const [conversations, setConversations] = useState([]);
useEffect(() => {
if (hasUniqueIdentifier) {
listConversations().then(setConversations);
}
}, [hasUniqueIdentifier]);
if (!hasUniqueIdentifier) {
return <p>Sign in to see conversation history</p>;
}
return (
<ul>
{conversations.map(conv => (
<li key={conv.id} onClick={() => loadConversation(conv.id)}>
{conv.title || 'Untitled'} - {conv.updatedAt.toLocaleDateString()}
</li>
))}
</ul>
);
}Complete Example: Custom Chat Panel
import React, { useState } from 'react';
import { useChat } from '@cyborg-sdk/react';
function CustomChatPanel() {
const [input, setInput] = useState('');
const {
messages,
isLoading,
error,
sendMessage,
clearMessages,
startNewConversation,
openChat,
closeChat,
isOpen
} = 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">
{/* Chat Toggle */}
<button onClick={isOpen ? closeChat : openChat}>
{isOpen ? 'Close' : 'Open'} Chat
</button>
{/* Error Display */}
{error && (
<div className="error-banner">
Error: {error.message}
</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}>
<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={startNewConversation}>
New Chat
</button>
<button type="button" onClick={clearMessages}>
Clear
</button>
</form>
</div>
);
}TypeScript Support
Full TypeScript support is built-in:
import { useChat, Message, CyborgChatContextValue } from '@cyborg-sdk/react';
function TypedChatComponent() {
const chat: CyborgChatContextValue = 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>;
case 'tool':
return <div className="tool-msg">{msg.content}</div>;
}
};
return (
<div>
{chat.messages.map(msg => (
<div key={msg.id}>{renderMessage(msg)}</div>
))}
</div>
);
}Context Requirement
The hook must be used within a component wrapped by CyborgProvider. Using it outside will throw an error:
// ❌ This will fail - not inside CyborgProvider
function InvalidUsage() {
const chat = useChat(); // Error: useChat must be used within a CyborgProvider
return null;
}
// ✅ This works - inside CyborgProvider
function App() {
return (
<CyborgProvider config={{ publishableKey: 'YOUR_API_KEY' }}>
<ValidUsage />
</CyborgProvider>
);
}
function ValidUsage() {
const { messages } = useChat(); // ✅ Works
return <div>{messages.length} messages</div>;
}Related Documentation
- CyborgProvider - Main provider component
- useCyborg Hook - Register context, tools, and instructions
- Conversation History Guide - In-depth history examples