Skip to content

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

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

typescript
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:

typescript
interface Message {
  id: string;
  content: string;
  role: 'user' | 'assistant' | 'system' | 'tool';
  timestamp: Date;
  toolCallId?: string;
  metadata?: Record<string, unknown>;
}

Example:

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

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

tsx
const { clearMessages } = useChat();

<button onClick={clearMessages}>Clear Chat</button>

startNewConversation(): void

Start a new conversation (clears messages and resets conversationId locally).

tsx
const { startNewConversation } = useChat();

<button onClick={startNewConversation}>New Chat</button>

openChat(): void

Open the chat widget.

tsx
const { openChat } = useChat();

<button onClick={openChat}>Open Chat</button>

closeChat(): void

Close the chat widget.

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

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

typescript
interface TriggerPromptOptions {
  startNew?: boolean;   // Whether to start a new conversation. Default: true
  instruction?: string; // Additional instruction for this prompt
}

Example: Help Buttons

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

tsx
const { loadConversation } = useChat();

await loadConversation('conv_abc123');

listConversations(): Promise<ConversationSummary[]>

List all conversations for the current user. Requires uniqueIdentifier to be set.

tsx
const { listConversations, loadConversation } = useChat();

const conversations = await listConversations();
// Returns: [{ id, title, createdAt, updatedAt }]

// Load a specific conversation
await loadConversation(conversations[0].id);

ConversationSummary type:

typescript
interface ConversationSummary {
  id: string;
  title?: string;
  createdAt: Date;
  updatedAt: Date;
}

Conversation History

When uniqueIdentifier is set in config, conversation history is enabled:

tsx
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

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

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

tsx
// ❌ 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>;
}

Built with VitePress