Skip to content

ChatHistory Component

Displays conversation history sidebar.

Import

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

Usage

tsx
<ChatHistory
  conversations={conversations}
  currentConversationId={currentId}
  onSelect={handleSelect}
  onDelete={handleDelete}
  onNew={handleNew}
/>

Props

PropTypeDescription
conversationsConversation[]List of conversations
currentConversationIdstringCurrently active conversation
isLoadingbooleanLoading state
hasMorebooleanMore items to load
onSelect(conv: Conversation) => voidSelection callback
onDelete(id: string) => voidDelete callback
onNew() => voidNew conversation callback
onLoadMore() => voidLoad more callback
renderItemFunctionCustom item renderer
classNamestringContainer class name

Conversation Structure

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

Examples

Basic

tsx
<ChatHistory
  conversations={conversations}
  currentConversationId={currentConv?.id}
  onSelect={(conv) => loadConversation(conv.id)}
  onDelete={(id) => deleteConversation(id)}
  onNew={() => createConversation()}
/>

With Load More

tsx
<ChatHistory
  conversations={conversations}
  hasMore={hasMoreConversations}
  onLoadMore={loadMoreConversations}
  onSelect={handleSelect}
  onDelete={handleDelete}
  onNew={handleNew}
/>

Custom Item Rendering

tsx
<ChatHistory
  conversations={conversations}
  renderItem={({ conversation, isActive, onSelect, onDelete }) => (
    <div
      className={`item ${isActive ? "active" : ""}`}
      onClick={onSelect}
    >
      <span className="title">
        {conversation.title || "Untitled"}
      </span>
      <span className="date">
        {formatDate(conversation.updatedAt)}
      </span>
      <button onClick={(e) => { e.stopPropagation(); onDelete(); }}>
        🗑️
      </button>
    </div>
  )}
/>
tsx
function SearchableHistory() {
  const [search, setSearch] = useState("");
  const filtered = conversations.filter(c =>
    c.title?.toLowerCase().includes(search.toLowerCase())
  );

  return (
    <div>
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search..."
      />
      <ChatHistory
        conversations={filtered}
        onSelect={handleSelect}
        onDelete={handleDelete}
        onNew={handleNew}
      />
    </div>
  );
}

Collapsible Sidebar

tsx
function CollapsibleHistory() {
  const [collapsed, setCollapsed] = useState(false);

  return (
    <div className={collapsed ? "w-16" : "w-64"}>
      <button onClick={() => setCollapsed(!collapsed)}>
        {collapsed ? "→" : "←"}
      </button>
      
      {!collapsed && (
        <ChatHistory
          conversations={conversations}
          onSelect={handleSelect}
          onDelete={handleDelete}
          onNew={handleNew}
        />
      )}
    </div>
  );
}

Styling

css
.chat-history {
  width: 260px;
  height: 100%;
  background: var(--chat-bg-secondary);
  border-right: 1px solid var(--chat-border-primary);
  display: flex;
  flex-direction: column;
}

.chat-history-header {
  padding: 16px;
  border-bottom: 1px solid var(--chat-border-secondary);
}

.chat-history-list {
  flex: 1;
  overflow-y: auto;
}

.chat-history-item {
  padding: 12px 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}

.chat-history-item:hover {
  background: var(--chat-bg-hover);
}

.chat-history-item.active {
  background: var(--chat-bg-tertiary);
  border-left: 3px solid var(--chat-accent-primary);
}

Released under the MIT License.