Hooks
Chat UI provides React hooks for building custom chat interfaces.
Available Hooks
| Hook | Description |
|---|---|
| useChat | Main chat hook with message handling |
| useChatHistory | Conversation history management |
| useChatContext | Access shared chat state/actions from ChatProvider |
| useChatContextOptional | Optional context accessor without throwing |
useI18n | Translate a key, switch locale, format numbers and dates (works without a provider, in English) |
useBackgroundAlerts | Tab-title badge, chime and desktop notification while the tab is hidden |
Two integration modes
Chat UI now supports two complementary patterns:
- API-first (default): Use
Chat/ChatMinimalfor fastest setup. - React-controlled: Use
ChatProvider + useChatContextto 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:
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>
);
}useChatHistory
Hook for managing conversation history:
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
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 localeOutside 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:
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 clickThe 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:
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:
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
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
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;
}
