---
title: Threads
description: "Manage persistent, resumable conversations with useThreads and the Enterprise Intelligence Platform — list, create, rename, archive, and delete threads with realtime sync across devices."
icon: "lucide/MessageSquareMore"
doc_type: how-to
---

import { Tabs, Tab } from "fumadocs-ui/components/tabs";
import {
  TailoredContent,
  TailoredContentOption,
} from "@/components/react/tailored-content.tsx";


## What is this?

CopilotKit threads enable persistent, resumable multi-turn conversations. The `useThreads` hook lists, creates, renames, archives, and deletes threads with realtime synchronization via WebSocket. Threads work with any agent framework — the Enterprise Intelligence Platform stores conversation history server-side, so users can close their browser and pick up where they left off. Thread metadata updates (renames, archives, new threads) are pushed to all connected clients in realtime without polling.

<OpsPlatformCTA
  variant="inline"
  title="Threads run on the Enterprise Intelligence Platform"
  body="Get persistent threads, realtime sync, and observability on the free Developer tier."
  surface="docs_threads"
/>

## When should I use this?

- Your app needs multiple saved conversations per user (like a chat history sidebar)
- Users should be able to resume a prior conversation across sessions or devices
- You want realtime sync so threads created on one tab appear on another
- You need to let users organize conversations by renaming or archiving them

## Prerequisites

- The Enterprise Intelligence Platform
- An OpenAI API key
- For the manual path: an existing CopilotKit project on `@copilotkit/react-core` v1.56+

## Implementation

<Steps>
  <TailoredContent
    className="step"
    id="threads-path"
    header={
      <div>
        <p className="text-xl font-semibold">Choose your starting point</p>
        <p className="text-base">
          Bootstrap a new project pre-wired for threads, or wire threads into an existing CopilotKit app.
        </p>
      </div>
    }
  >
    <TailoredContentOption
      id="cli"
      title="Start with the CLI"
      description="Bootstrap a new full-stack project pre-configured for threads."
    >
      <Step>
        ### Run the CLI

        Bootstrap a new project pre-configured for threads. Follow the interactive prompts to choose a project name, enable Intelligence, and the CLI will install a starter project and dependencies.

        ```bash
        # When prompted, choose: "Yes — enable Intelligence (threads, persistence, insights)"
        npx -y @copilotkit/cli@latest create
        ```
      </Step>

      <Step>
        ### Move into your project

        ```bash
        cd your-project-name
        ```
      </Step>

      <Step>
        ### Install dependencies

        ```npm
        npm install
        ```
      </Step>

      <Step>
        ### Add your OpenAI API key

        The example uses OpenAI to generate responses, so it needs an API key. Put it in `.env`:

        ```plaintext title=".env"
        OPENAI_API_KEY=sk-your_openai_api_key
        ```
      </Step>

      <Step>
        ### Start the development environment

        <Tabs groupId="package-manager" items={['npm', 'pnpm', 'yarn', 'bun']}>
          <Tab value="npm">
            ```bash
            npm run dev
            ```
          </Tab>
          <Tab value="pnpm">
            ```bash
            pnpm dev
            ```
          </Tab>
          <Tab value="yarn">
            ```bash
            yarn dev
            ```
          </Tab>
          <Tab value="bun">
            ```bash
            bun dev
            ```
          </Tab>
        </Tabs>

        This boots three things concurrently:

        - **A local instance of the Enterprise Intelligence Platform** — stores threads and powers realtime sync
        - **A BFF (backend-for-frontend)** — your CopilotKit runtime, wired to the local Enterprise Intelligence Platform
        - **The web app** — reachable at [http://localhost:3000](http://localhost:3000)

        Open the app, send a message, and the conversation will be saved as a thread you can resume later.
      </Step>
    </TailoredContentOption>

    <TailoredContentOption
      id="manual"
      title="Add to an existing app"
      description="Wire threads into a project you already have."
    >
      <Step>
        ### Configure your runtime with the Enterprise Intelligence Platform

        Connect your CopilotKit runtime to the Enterprise Intelligence Platform. This provides the thread storage and WebSocket infrastructure. Thread names are automatically generated by the LLM after the first message — you can disable this with `generateThreadNames: false`.

        ```typescript title="server.ts"
        import { CopilotRuntime } from "@copilotkit/runtime";

        const runtime = new CopilotRuntime({
          agents: {
            default: agent,
          },
          // Thread names are auto-generated by default.
          // Set to false to disable:
          // generateThreadNames: false,

          // Optional: tune thread lock behavior
          // lockTtlSeconds: 20,              // Lock TTL (default 20s, max 3600s)
          // lockHeartbeatIntervalSeconds: 15, // Heartbeat interval (default 15s, max 3000s)
          // lockKeyPrefix: "my-app",          // Custom Redis key prefix for the lock
        });
        ```

        If you're using Copilot Cloud, thread storage is handled automatically. For self-hosted deployments, see [Self-Hosting Intelligence](/premium/self-hosting) for the Helm chart install and database configuration.

        **Thread lock options:** When an agent run starts, the runtime acquires a lock on the thread to prevent concurrent runs. You can tune this behavior:

        | Option | Default | Max | Description |
        |--------|---------|-----|-------------|
        | `lockTtlSeconds` | `20` | `3600` (1 hour) | How long the lock is held before it expires automatically. |
        | `lockHeartbeatIntervalSeconds` | `15` | `3000` (50 min) | How often the runtime renews the lock during a run. |
        | `lockKeyPrefix` | — | — | Custom Redis key prefix for the thread lock. Useful when multiple apps share a Redis instance. |
      </Step>

      <Step>
        ### List and manage threads with useThreads

        Use the `useThreads` hook to fetch and manage threads for a specific agent. The hook returns the thread list, loading state, and mutation methods.

        ```tsx title="ThreadSidebar.tsx"
        import { useThreads } from "@copilotkit/react-core/v2"; // [!code highlight]

        function ThreadSidebar() {
          const { // [!code highlight:6]
            threads,
            isLoading,
            renameThread,
            archiveThread,
            deleteThread,
          } = useThreads({ agentId: "my-agent" });

          if (isLoading) return <div>Loading...</div>;

          return (
            <div>
              {threads.map((thread) => (
                <div key={thread.id}>
                  <span>{thread.name ?? "New conversation"}</span>
                  <button onClick={() => renameThread(thread.id, "Renamed")}>
                    Rename
                  </button>
                  <button onClick={() => archiveThread(thread.id)}>
                    Archive
                  </button>
                </div>
              ))}
            </div>
          );
        }
        ```

        The `threads` array is sorted by most recently updated first and stays synchronized in realtime — new threads from other tabs or devices appear automatically.

        **Archive vs. delete:** `archiveThread` is a soft delete — the thread stays in the database but is hidden from the list by default. Pass `includeArchived: true` to show archived threads. `deleteThread` is permanent and irreversible. Neither has a built-in confirmation dialog — add your own if needed.
      </Step>

      <Step>
        ### Switch between threads

        When a user selects a thread, pass its `threadId` to your chat component. The chat clears the current messages, fetches the selected thread's history, and replays it. If the agent is still running on that thread, the chat picks up the live stream.

        ```tsx title="App.tsx"
        import { CopilotChat } from "@copilotkit/react-core/v2"; // [!code highlight]
        import { useState } from "react";

        function App() {
          const [activeThreadId, setActiveThreadId] = useState<string | undefined>();

          return (
            <div className="flex">
              <ThreadSidebar onSelectThread={setActiveThreadId} />
              <CopilotChat threadId={activeThreadId} /> {/* [!code highlight] */}
            </div>
          );
        }
        ```

        When `threadId` changes, the chat component automatically loads the selected thread's history and reconnects to the agent's stream. If the agent is still running on that thread, the chat picks up the live stream.
      </Step>

      <Step>
        ### Add pagination for large thread lists

        For users with many conversations, use the `limit` parameter to enable cursor-based pagination.

        ```tsx title="ThreadSidebar.tsx"
        const {
          threads,
          hasMoreThreads,
          isFetchingMoreThreads,
          fetchMoreThreads,
        } = useThreads({
          agentId: "my-agent",
          limit: 20, // [!code highlight]
        });

        // In your JSX:
        {hasMoreThreads && (
          <button
            onClick={fetchMoreThreads}
            disabled={isFetchingMoreThreads}
          >
            {isFetchingMoreThreads ? "Loading..." : "Load more"}
          </button>
        )}
        ```
      </Step>
    </TailoredContentOption>
  </TailoredContent>
</Steps>

## Next steps

- **Understand how it works:** [How Threads & Persistence Work](/learn/threads) — architecture, event replay model, and WebSocket sync
- **API reference:** [useThreads](/reference/v2/hooks/useThreads) — parameters, return values, types
- **Tutorial:** [Build a Multi-Conversation Chat App](/learn/tutorials/multi-conversation-chat) — end-to-end walkthrough building a chat app with thread history

