Skip to content

Hooks

Chat UI provides React hooks for building custom chat interfaces.

Available Hooks

HookDescription
useChatMain chat hook with message handling
useChatHistoryConversation history management
useChatContextAccess shared chat state/actions from ChatProvider
useChatContextOptionalOptional context accessor without throwing

Two integration modes

Chat UI now supports two complementary patterns:

  1. API-first (default): Use Chat / ChatMinimal for fastest setup.
  2. React-controlled: Use ChatProvider + useChatContext to control flow from any React component.

Choose React-controlled mode when you want to orchestrate chat from multiple components (custom toolbar, side panels, feature flags, local app state sync).

useChat

The main hook for chat functionality:

tsx
import { useChat } from "@cognipeer/chat-ui";

function CustomChat() {
  const {
    messages,
    isLoading,
    streamingText,
    error,
    sendMessage,
    stop,
    retry,
    conversation,
    createConversation,
    loadConversation,
  } = useChat({
    baseUrl: "/api/agents",
    agentId: "assistant",
    authorization: "Bearer token",
  });

  return (
    <div>
      {messages.map(msg => (
        <div key={msg.id}>{msg.content}</div>
      ))}
      
      {isLoading && <div>{streamingText || "Loading..."}</div>}
      
      <input
        onKeyPress={(e) => {
          if (e.key === "Enter") {
            sendMessage(e.currentTarget.value);
            e.currentTarget.value = "";
          }
        }}
      />
    </div>
  );
}

Full useChat documentation →

useChatHistory

Hook for managing conversation history:

tsx
import { useChatHistory } from "@cognipeer/chat-ui";

function HistorySidebar() {
  const {
    conversations,
    isLoading,
    hasMore,
    load,
    loadMore,
    deleteConversation,
    refresh,
  } = useChatHistory({
    baseUrl: "/api/agents",
    agentId: "assistant",
  });

  return (
    <div>
      {conversations.map(conv => (
        <div key={conv.id}>
          {conv.title}
          <button onClick={() => deleteConversation(conv.id)}>
            Delete
          </button>
        </div>
      ))}
      
      {hasMore && (
        <button onClick={loadMore}>Load More</button>
      )}
    </div>
  );
}

Full useChatHistory documentation →

Combining Hooks

Use both hooks together for a complete implementation:

tsx
import { useChat, useChatHistory, ChatMessageList, ChatInput } from "@cognipeer/chat-ui";

function CustomChatApp() {
  const chat = useChat({
    baseUrl: "/api/agents",
    agentId: "assistant",
  });
  
  const history = useChatHistory({
    baseUrl: "/api/agents",
    agentId: "assistant",
  });

  return (
    <div className="flex h-screen">
      {/* Sidebar */}
      <div className="w-64 border-r">
        <button onClick={() => chat.createConversation()}>
          New Chat
        </button>
        
        {history.conversations.map(conv => (
          <div
            key={conv.id}
            onClick={() => chat.loadConversation(conv.id)}
            className={conv.id === chat.conversation?.id ? "active" : ""}
          >
            {conv.title || "Untitled"}
          </div>
        ))}
      </div>
      
      {/* Chat area */}
      <div className="flex-1 flex flex-col">
        <ChatMessageList
          messages={chat.messages}
          isLoading={chat.isLoading}
          streamingText={chat.streamingText}
        />
        
        <ChatInput
          onSubmit={chat.sendMessage}
          isLoading={chat.isLoading}
          onStop={chat.stop}
        />
      </div>
    </div>
  );
}

Context + Hooks composition

When you want shared access, wrap your page with ChatProvider, then consume from child components:

tsx
import {
  ChatProvider,
  useChatContext,
  useChatHistory,
  ChatMessageList,
  ChatInput,
} from "@cognipeer/chat-ui";

function ChatBody() {
  const chat = useChatContext();
  const history = useChatHistory({
    baseUrl: "/api/agents",
    agentId: "assistant",
  });

  return (
    <>
      <button onClick={() => history.refresh()}>Refresh History</button>
      <ChatMessageList
        messages={chat.messages}
        isStreaming={chat.isStreaming}
        streamingText={chat.streamingText}
      />
      <ChatInput
        onSend={chat.sendMessage}
        onStop={chat.stop}
        isLoading={chat.isStreaming}
      />
    </>
  );
}

export default function Page() {
  return (
    <ChatProvider baseUrl="/api/agents" agentId="assistant">
      <ChatBody />
    </ChatProvider>
  );
}

Custom Hook Patterns

With Local Storage

tsx
import { useChat } from "@cognipeer/chat-ui";
import { useEffect } from "react";

function useChatWithPersistence(options) {
  const chat = useChat(options);

  // Persist last conversation ID
  useEffect(() => {
    if (chat.conversation?.id) {
      localStorage.setItem("lastConversationId", chat.conversation.id);
    }
  }, [chat.conversation?.id]);

  // Restore last conversation
  useEffect(() => {
    const lastId = localStorage.getItem("lastConversationId");
    if (lastId) {
      chat.loadConversation(lastId);
    }
  }, []);

  return chat;
}

With Analytics

tsx
import { useChat } from "@cognipeer/chat-ui";

function useChatWithAnalytics(options) {
  const chat = useChat({
    ...options,
    onMessageSent: (message) => {
      analytics.track("message_sent", { length: message.content.length });
      options.onMessageSent?.(message);
    },
    onMessageReceived: (message) => {
      analytics.track("message_received");
      options.onMessageReceived?.(message);
    },
    onError: (error) => {
      analytics.track("chat_error", { message: error.message });
      options.onError?.(error);
    },
  });

  return chat;
}

Next Steps

Released under the MIT License.