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

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

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

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

Example:

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

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

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

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(''); // 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:

tsx
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

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

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

tsx
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

tsx
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

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

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

Built with VitePress