showcase/shell-docs/src/content/reference/components/CopilotThreadsDrawer.mdx
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 { CopilotThreadsDrawer } from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
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.
Project light-DOM children with a slot attribute to replace a region:
| Slot | Replaces |
|---|---|
header | The drawer header region (renders only when you project content). |
empty | The empty-state body shown when there are no threads. |
footer | A footer region (hidden until you project content). |
memories | A reserved region for a future memories surface. |
launcher-icon | The 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>
Style structural elements with ::part():
root, header, list, footerrow, row-active (the selected row), row-name, row-menu (kebab), row-menu-popover, row-archive, row-unarchive, row-deletenew-thread-button, section-headingfilter-toggle, filter-indicator, filter-active, filter-all, filterscollapse-toggle (desktop), close-toggle (mobile), backdrop (mobile scrim), launcher, launcher-cluster, launcher-new-threadload-more, fetching-more, fetch-more-error, fetch-more-retryloading, empty, error, retry-button, licensed, licensed-cta, memoriesconfirm-dialog, confirm-cancel, confirm-deleteThe 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.
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.
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>
);
}
function App() {
return (
<CopilotThreadsDrawer
label="History"
renderRow={(thread) => <em>{thread.name ?? "New conversation"}</em>}
/>
);
}
onThreadSelect/onNewThread, the drawer drives the chat configuration — select connects + replays; the "New Conversation" row shows the welcome screen.--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-menu), not always-visible inline buttons; delete still asks for confirmation.launcher-icon slot) and position (--cpk-drawer-launcher-*) are customizable.useThreads — the headless thread data + mutationsCopilotChat — the chat the drawer connects to