showcase/shell-docs/src/content/snippets/shared/basics/copilot-threads-drawer.mdx
<CopilotThreadsDrawer> gives your app a ready-made conversation sidebar. Users can
list, switch, start, archive, and delete their threads, and you wire up
no active-thread state to make it work. Drop it next to your chat and it
connects automatically.
Start with the Threads overview to compare the Drawer with a custom headless UI and understand the shared persistence infrastructure beneath both.
<Frame description="CopilotThreadsDrawer beside a CopilotChat. Selecting a thread replays its conversation with no active-thread wiring."> </Frame>Under the hood it's a thin React wrapper around the self-contained
copilotkit-threads-drawer web component, fed by useThreads
and your chat configuration.
Prefer to build your own thread UI? The same data and actions are available
headlessly through the useThreads hook (see
Headless Threads). Thread rename is one of those headless
actions: the prebuilt drawer's row menu covers archive/unarchive and delete, so
wire up rename with useThreads and your own UI if you need it.
Use the drawer when you want:
It requires the Enterprise Intelligence Platform (threads are stored and synced server-side). <SignupLink surface="docs_drawer">Get a free developer account</SignupLink> to set that up.
<OpsPlatformCTA variant="inline" title="Threads run on the Enterprise Intelligence Platform" body="Get persistent threads and realtime sync on the free Developer tier." surface="docs_drawer" />
Drop <CopilotThreadsDrawer> next to <CopilotChat>, and wrap both in a
shared <CopilotChatConfigurationProvider> so they operate on the same chat
configuration. That shared config is what lets the drawer drive the chat: with
no callbacks, 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.
(Without the shared provider the drawer sits beside the chat's own scoped
configuration and can't switch its thread.)
import {
CopilotKitProvider,
CopilotChatConfigurationProvider,
CopilotChat,
CopilotThreadsDrawer,
} from "@copilotkit/react-core/v2";
import "@copilotkit/react-core/v2/styles.css";
export default function Page() {
return (
<CopilotKitProvider runtimeUrl="/api/copilotkit" publicLicenseKey="ck_pub_...">
<CopilotChatConfigurationProvider>
<div style={{ display: "flex", height: "100dvh" }}>
<CopilotThreadsDrawer />
<CopilotChat />
</div>
</CopilotChatConfigurationProvider>
</CopilotKitProvider>
);
}
That's the whole integration. A hand-rolled sidebar would have you track the
active threadId in your own state and pass selection/new-thread handlers
between the list and the chat; here the drawer and the
CopilotChatConfigurationProvider share that state, so there is none of that
wiring to write.
<CopilotThreadsDrawer> works with zero props. The optional ones:
| Prop | Description |
|---|---|
agentId | Agent whose threads to list (defaults to the chat configuration's agent). |
label | Accessible name for the drawer region and thread listbox. Defaults to "Threads". The redesigned header has no visible title. |
recentLabel | Section heading shown above the thread list. Defaults to "Recent Conversations". |
onThreadSelect | Escape hatch to take over thread selection yourself. |
onNewThread | Escape hatch to handle the "New Conversation" row yourself. |
renderRow | Render custom content per row (keeps the row chrome around it). |
limit | Page size for pagination. Shows a "Load more" control while more threads remain. Omit to load all at once. |
See the Threads Drawer reference for the full prop and type list.
The drawer renders in a shadow root with self-contained, theme-inheriting styles, so it looks correct in any app (including dark mode) with no config. Three bounded escape hatches pierce the boundary:
Slots: project children with a slot attribute (header, empty, footer, memories, launcher-icon):
<CopilotThreadsDrawer>
<span slot="header">My conversations</span>
</CopilotThreadsDrawer>
Per-row content: the renderRow prop projects custom content into each row while the element keeps selection, archived styling, and the per-row kebab menu (Archive/Delete).
CSS parts + variable tokens: restyle structural ::part()s or override --cpk-drawer-* tokens. The full list is in the reference.
Threads require CopilotKit Intelligence. Without a license key, the drawer shows a locked view in place of the list.
useThreads data layer