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
useI18nTranslate a key, switch locale, format numbers and dates (works without a provider, in English)
useBackgroundAlertsTab-title badge, chime and desktop notification while the tab is hidden

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 →

useI18n ​

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

const { t, has, locale, setLocale, formatNumber, formatDate, formatRelativeTime } = useI18n();

t("chat.input.placeholder");                       // the string for the current locale (English fallback per key)
has("chat.error.code.rate_limited");               // is there a string for this key?
setLocale("tr");                                   // also writes ?lang=tr to the URL
formatRelativeTime(-3, "hour");                    // "3 hours ago" in the current locale

Outside a ChatI18nProvider it answers in English and setLocale has no effect. See Internationalization.

useBackgroundAlerts ​

Chat uses it for its notifications prop; use it directly in a custom layout:

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

const alerts = useBackgroundAlerts({
  config: { tabBadge: true, sound: false, desktop: "opt_in" },
  busy: chat.isStreaming,           // a turn is running: when it ends while the tab is hidden, that is an alert
  waiting: chat.awaitingAction,     // an approval or a question waits for the person
  heading: chat.conversation?.title ?? "Support",
  doneText: "The answer is ready.",
  waitingText: "The assistant is waiting for you.",
});

alerts.count;        // unseen alerts, shown as "(2) Support" in the tab title
alerts.available;    // the bell menu has something to offer
alerts.prefs;        // { sound, desktop } as the person chose them (kept in localStorage under "cp:notify:prefs")
alerts.setDesktop(true);   // asks the browser for permission, only now, on the person's click

The tab badge is cleared when the person looks at the tab. formatBadgeTitle(base, count) builds the title.

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}
          isStreaming={chat.isStreaming}
          streamingText={chat.streamingText}
          actions={{ onResolve: chat.resolveAction, canResolve: chat.canResolveActions }}
        />
        
        <ChatInput
          onSend={chat.sendMessage}
          isLoading={chat.isStreaming}
          onStop={chat.stop}
          state={chat.composerState}
        />
      </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 ​

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub