CopilotThreadsDrawer

Prebuilt threads drawer that lists, switches, and manages conversations next to CopilotChat


Overview

CopilotThreadsDrawer is a prebuilt thread switcher. Dropped in next to CopilotChat, it lists the user's conversations and lets them switch, start, archive, and delete threads — with no active-thread state to wire up. (Thread rename is available headlessly via useThreads, not through the drawer's row menu.) It is a thin React wrapper around the framework-agnostic <copilotkit-threads-drawer> shadow-DOM web component, fed by the useThreads hook and the surrounding chat configuration.

When onThreadSelect / onNewThread are omitted, the drawer drives the surrounding chat configuration directly: selecting a row connects the chat to that thread (and replays its history); the "New Conversation" row resets the chat to a fresh welcome screen. Pass the callbacks only to take control of the active thread yourself.

See the CopilotThreadsDrawer guide for a walkthrough, and useThreads for the headless data layer underneath.

Import

import { CopilotThreadsDrawer } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";

Props

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Prop

Type

Customization

The drawer renders inside a shadow root and ships its own self-contained, theme-inheriting styles, so it looks correct in any host (including dark mode) with zero config. Three bounded escape hatches pierce the boundary.

Slots

Project light-DOM children with a slot attribute to replace a region:

SlotReplaces
headerThe drawer header region (renders only when you project content).
emptyThe empty-state body shown when there are no threads.
footerA footer region (hidden until you project content).
memoriesA reserved region for a future memories surface.
launcher-iconThe icon inside the floating mobile launcher.
row:{id}Per-row content — prefer the renderRow prop, which manages this.
<CopilotThreadsDrawer>
  <span slot="header">My conversations</span>
</CopilotThreadsDrawer>

CSS parts

Style structural elements with ::part():

  • Structureroot, header, list, footer
  • Rowsrow, row-active (the selected row), row-name, row-menu (kebab), row-menu-popover, row-archive, row-unarchive, row-delete
  • New conversation & sectionsnew-thread-button, section-heading
  • Filterfilter-toggle, filter-indicator, filter-active, filter-all, filters
  • Collapse, mobile & launchercollapse-toggle (desktop), close-toggle (mobile), backdrop (mobile scrim), launcher, launcher-cluster, launcher-new-thread
  • Paginationload-more, fetching-more, fetch-more-error, fetch-more-retry
  • States & upsellloading, empty, error, retry-button, licensed, licensed-cta, memories
  • Delete confirm dialogconfirm-dialog, confirm-cancel, confirm-delete

The Active/All filter lives behind the filter-toggle funnel, whose filter-indicator dot shows when a non-default filter is applied. Per-row Archive/Delete actions live in the row-menu kebab and its row-menu-popover. When the list is collapsed on desktop (or closed on mobile), the floating launcher-cluster holds the launcher toggle and the launcher-new-thread button.

CSS variable tokens

Tokens override the bundled defaults and fall back to the host theme's vars (so the drawer follows light/dark automatically): --cpk-drawer-bg, --cpk-drawer-fg, --cpk-drawer-border, --cpk-drawer-surface, --cpk-drawer-surface-fg, --cpk-drawer-muted, --cpk-drawer-muted-fg, --cpk-drawer-accent, --cpk-drawer-accent-fg, --cpk-drawer-primary, --cpk-drawer-primary-fg, --cpk-drawer-danger, --cpk-drawer-indicator (default #5b94e4, color of the filter-applied dot), --cpk-drawer-ring, --cpk-drawer-radius, --cpk-drawer-width, --cpk-drawer-font-family, --cpk-drawer-font-size, --cpk-drawer-line-height, --cpk-drawer-launcher-top, --cpk-drawer-launcher-left.

Usage

Basic usage

Wrap the drawer and CopilotChat in a shared CopilotChatConfigurationProvider so they operate on the same chat configuration — that shared config is how selecting a thread (or "New Conversation") drives the chat. Without a shared provider the drawer sits beside the chat's own scoped configuration and can't switch its thread.

import {
  CopilotThreadsDrawer,
  CopilotChat,
  CopilotChatConfigurationProvider,
} from "@copilotkit/react-core/v2";

function App() {
  return (
    <CopilotChatConfigurationProvider>
      <div style={{ display: "flex", height: "100%" }}>
        <CopilotThreadsDrawer />
        <CopilotChat />
      </div>
    </CopilotChatConfigurationProvider>
  );
}

Custom row content + label

function App() {
  return (
    <CopilotThreadsDrawer
      label="History"
      renderRow={(thread) => <em>{thread.name ?? "New conversation"}</em>}
    />
  );
}

Behavior

  • Self-connecting: without onThreadSelect/onNewThread, the drawer drives the chat configuration — select connects + replays; the "New Conversation" row shows the welcome screen.
  • Collapsible sidebar: on larger screens the drawer is an expanded sidebar by default with a collapse toggle (sidebar glyph) in the header. Collapsing replaces the panel with a small floating cluster — a sidebar toggle to expand plus a "New Conversation" button — and (via --cpk-drawer-reserved-width) lets a host grid reclaim the column. Set collapsible={false} to omit the toggle. "New Conversation" is its own row below the header; project slot="header" content to add your own chrome beside the toggle.
  • Row actions: per-row Archive/Delete live behind a per-row kebab (⋮) menu (row-menu), not always-visible inline buttons; delete still asks for confirmation.
  • Optimistic mutations: archive/unarchive/rename/delete are optimistic in the core thread store; delete rolls back if the server rejects.
  • Deleting the active thread resets to a fresh thread; archiving the active thread keeps you viewing it. Archived threads render italic/muted inline in the "All" view.
  • Filter: an Active/All filter lives behind a funnel icon popover under the "Recent Conversations" heading; a small indicator dot appears on the funnel when a non-default filter is applied. Toggling the filter refetches server-side.
  • Mobile: below 768px the drawer is an off-canvas modal. Closed, it shows the same floating cluster (a launcher to open it + a "New Conversation" button); open, it slides in and is dismissed by tapping the backdrop or pressing Escape. The launcher icon (launcher-icon slot) and position (--cpk-drawer-launcher-*) are customizable.
  • License: threads require CopilotKit Intelligence; without a license key the drawer shows a locked view in place of the list.

Related