7acadae
CopilotKitDocs
  • Docs
  • Integrations
  • Reference
Get Started
QuickstartCoding Agents
Concepts
ArchitectureGenerative UI OverviewOSS vs Enterprise
Agentic Protocols
OverviewAG-UIAG-UI MiddlewareMCPA2A
Build Chat UIs
Prebuilt Components
CopilotChatCopilotSidebarCopilotPopup
Custom Look and Feel
CSS CustomizationSlots (Subcomponents)Fully Headless UIReasoning Messages
Multimodal AttachmentsVoice
Build Generative UI
Controlled
Tool-based Generative UITool RenderingState RenderingReasoning
Your Components
Display ComponentsInteractive Components
Declarative
A2UIDynamic Schema A2UIFixed Schema A2UI
Open-Ended
MCP Apps
Adding Agent Powers
Frontend ToolsShared State
Human-in-the-Loop
HITL OverviewPausing the Agent for InputHeadless Interrupts
Sub-AgentsAgent ConfigProgrammatic Control
Agents & Backends
Built-in Agent
Backend
Copilot RuntimeFactory ModeAG-UI
Runtime Server AdapterAuthentication
Built-in Agent (TanStack AI)
Advanced ConfigurationMCP ServersModel SelectionServer Tools
Observe & Operate
InspectorVS Code Extension
Troubleshooting
Common Copilot IssuesError Debugging & ObservabilityDebug ModeAG-UI Event InspectorHook ExplorerError Observability Connectors
Enterprise
CopilotKit PremiumHow the Enterprise Intelligence Platform WorksHow Threads & Persistence WorkObservabilitySelf-Hosting IntelligenceThreads
Deploy
AWS AgentCore
What's New
Full MCP Apps SupportLangGraph Deep Agents in CopilotKitA2UI Launches with full AG-UI SupportCopilotKit v1.50Generative UI Spec SupportA2A and MCP Handshake
Migrate
Migrate to V2Migrate to 1.8.2
Other
Contributing
Code ContributionsDocumentation Contributions
Anonymous Telemetry
Built-in Agent (TanStack AI)Custom Look and FeelSlots (Subcomponents)

Slots (Subcomponents)

Customize any part of the chat UI by overriding individual sub-components via slots.

What is this?#

Every CopilotKit chat component is built from composable slots, named sub-components you can override individually. The slot system gives you three levels of customization without needing to rebuild the entire UI:

  1. Tailwind classes — pass a string to add/override CSS classes
  2. Props override — pass an object to override specific props on the default component
  3. Custom component — pass your own React component to fully replace a slot

Slots are recursive: you can drill into nested sub-components at any depth.

Live Demo: Built-in Agent (TanStack AI) — chat-slotsOpen full demo →

What it looks like in code#

The chat-slots cell above overrides three slots on a single <CopilotChat> — the welcome screen, the assistant message card, and the input's disclaimer. Each slot is just a prop; the demo extracts them into locals so the override points are easy to see.

Welcome screen slot#

The welcomeScreen prop replaces the empty-state view shown before the first message is sent. The demo swaps in a gradient card that still renders the default input and suggestions:

frontend/src/app/page.tsx — welcomeScreen slot
L3–40
import React from "react";
import {
  CopilotKitProvider,
  CopilotChat,
  CopilotChatAssistantMessage,
  useConfigureSuggestions,
} from "@copilotkit/react-core/v2";
import { CustomWelcomeScreen } from "./custom-welcome-screen";
import { CustomAssistantMessage } from "./custom-assistant-message";
import { CustomDisclaimer } from "./custom-disclaimer";

// Outer layer — provider + layout chrome.
export default function ChatSlotsDemo() {
  return (
    <CopilotKitProvider runtimeUrl="/api/copilotkit" useSingleEndpoint>
      <div className="flex justify-center items-center h-screen w-full">
        <div className="h-full w-full max-w-4xl">
          <Chat />
        </div>
      </div>
    </CopilotKitProvider>
  );
}

// The actual view — just the chat, with two slot overrides.
function Chat() {
  useConfigureSuggestions({
    suggestions: [
      { title: "Write a sonnet", message: "Write a short sonnet about AI." },
      { title: "Tell me a joke", message: "Tell me a short joke." },
    ],
    available: "always",
  });

  // Each slot is wired in as a prop on <CopilotChat>. Extracting the
  // overrides up here keeps the JSX readable and gives the docs something
  // to point at with `@region` markers for the slot system guide.
  const welcomeScreen = CustomWelcomeScreen;

Assistant message slot#

Drill into messageView={{ assistantMessage: ... }} to wrap every assistant response. The cell wraps the default component with a tinted card and a small "slot" badge so you can see the override is active during the message flow:

frontend/src/app/page.tsx — assistantMessage slot
L3–45
import React from "react";
import {
  CopilotKitProvider,
  CopilotChat,
  CopilotChatAssistantMessage,
  useConfigureSuggestions,
} from "@copilotkit/react-core/v2";
import { CustomWelcomeScreen } from "./custom-welcome-screen";
import { CustomAssistantMessage } from "./custom-assistant-message";
import { CustomDisclaimer } from "./custom-disclaimer";

// Outer layer — provider + layout chrome.
export default function ChatSlotsDemo() {
  return (
    <CopilotKitProvider runtimeUrl="/api/copilotkit" useSingleEndpoint>
      <div className="flex justify-center items-center h-screen w-full">
        <div className="h-full w-full max-w-4xl">
          <Chat />
        </div>
      </div>
    </CopilotKitProvider>
  );
}

// The actual view — just the chat, with two slot overrides.
function Chat() {
  useConfigureSuggestions({
    suggestions: [
      { title: "Write a sonnet", message: "Write a short sonnet about AI." },
      { title: "Tell me a joke", message: "Tell me a short joke." },
    ],
    available: "always",
  });

  // Each slot is wired in as a prop on <CopilotChat>. Extracting the
  // overrides up here keeps the JSX readable and gives the docs something
  // to point at with `@region` markers for the slot system guide.
  const welcomeScreen = CustomWelcomeScreen;
  const input = { disclaimer: CustomDisclaimer };
  const messageView = {
    assistantMessage:
      CustomAssistantMessage as unknown as typeof CopilotChatAssistantMessage,
  };

Disclaimer slot#

The input={{ disclaimer: ... }} sub-slot lets you replace the small text shown below the input. The demo uses it to display a visibly tagged disclaimer so reviewers can tell the override is still in effect once the welcome screen is gone:

frontend/src/app/page.tsx — disclaimer slot
L3–41
import React from "react";
import {
  CopilotKitProvider,
  CopilotChat,
  CopilotChatAssistantMessage,
  useConfigureSuggestions,
} from "@copilotkit/react-core/v2";
import { CustomWelcomeScreen } from "./custom-welcome-screen";
import { CustomAssistantMessage } from "./custom-assistant-message";
import { CustomDisclaimer } from "./custom-disclaimer";

// Outer layer — provider + layout chrome.
export default function ChatSlotsDemo() {
  return (
    <CopilotKitProvider runtimeUrl="/api/copilotkit" useSingleEndpoint>
      <div className="flex justify-center items-center h-screen w-full">
        <div className="h-full w-full max-w-4xl">
          <Chat />
        </div>
      </div>
    </CopilotKitProvider>
  );
}

// The actual view — just the chat, with two slot overrides.
function Chat() {
  useConfigureSuggestions({
    suggestions: [
      { title: "Write a sonnet", message: "Write a short sonnet about AI." },
      { title: "Tell me a joke", message: "Tell me a short joke." },
    ],
    available: "always",
  });

  // Each slot is wired in as a prop on <CopilotChat>. Extracting the
  // overrides up here keeps the JSX readable and gives the docs something
  // to point at with `@region` markers for the slot system guide.
  const welcomeScreen = CustomWelcomeScreen;
  const input = { disclaimer: CustomDisclaimer };

Tailwind Classes#

The simplest way to customize a slot. Pass a Tailwind class string and it will be merged with the default component's classes.

page.tsx
tsx
import { CopilotChat } from "@copilotkit/react-core/v2";

export function Chat() {
  return (
    <CopilotChat
      messageView="bg-gray-50 dark:bg-gray-900 p-4"
      input="border-2 border-blue-400 rounded-xl"
    />
  );
}

Props Override#

Pass an object to override specific props on the default component. This is useful for adding className, event handlers, data attributes, or any other prop the default component accepts.

page.tsx
tsx
<CopilotChat
  messageView={{
    className: "my-custom-messages",
    "data-testid": "message-view",
  }}
  input={{ autoFocus: true }}
/>

Custom Components#

For full control, pass your own React component. It receives all the same props as the default component.

page.tsx
tsx
import { CopilotChat } from "@copilotkit/react-core/v2";

const CustomMessageView = ({ messages, isRunning }) => (
  <div className="space-y-4 p-6">
    {messages?.map((msg) => (
      <div key={msg.id} className={msg.role === "user" ? "text-right" : "text-left"}>
        {msg.content}
      </div>
    ))}
    {isRunning && <div className="animate-pulse">Thinking...</div>}
  </div>
);

export function Chat() {
  return <CopilotChat messageView={CustomMessageView} />;
}

Nested Slots (Drill-Down)#

Slots are recursive. You can customize sub-components at any depth by nesting objects.

Two levels deep#

Override the assistant message's toolbar within the message view:

page.tsx
tsx
<CopilotChat
  messageView={{
    assistantMessage: {
      toolbar: CustomToolbar,
      copyButton: CustomCopyButton,
    },
    userMessage: CustomUserMessage,
  }}
/>

Three levels deep#

Override a specific button inside the assistant message toolbar:

page.tsx
tsx
<CopilotChat
  messageView={{
    assistantMessage: {
      copyButton: ({ onClick }) => (
        <button onClick={onClick}>Copy</button>
      ),
    },
  }}
/>

Labels#

Customize any text string in the UI via the labels prop. This is a separate convenience prop on CopilotChat, CopilotSidebar, and CopilotPopup, not part of the slot system.

page.tsx
tsx
<CopilotChat
  labels={{
    chatInputPlaceholder: "Ask your agent anything...",
    welcomeMessageText: "How can I help you today?",
    chatDisclaimerText: "AI responses may be inaccurate.",
  }}
/>

Available Slots#

CopilotChat / CopilotSidebar / CopilotPopup#

These are the root-level slot props available on all chat components:

SlotDescription
messageViewThe message list container.
scrollViewThe scroll container with auto-scroll behavior.
inputThe text input area with send/transcribe controls.
suggestionViewThe suggestion pills shown below messages.
welcomeScreenThe initial empty-state screen (pass false to disable).

CopilotSidebar and CopilotPopup also have:

SlotDescription
headerThe modal header bar.
toggleButtonThe open/close toggle button.