CopilotKit

Slots (Subcomponents)

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


"""Default LangGraph agent — neutral "helpful, concise assistant".This is the fallthrough graph for demos that don't require anything morespecialized. Cells that need tailored behavior (chart viz, weather-only,etc.) should have their own dedicated graph under `src/agents/` andexplicit wiring in the CopilotKit route."""from langchain.agents import create_agentfrom langchain_openai import ChatOpenAIfrom copilotkit import CopilotKitMiddlewaregraph = create_agent(    model=ChatOpenAI(model="gpt-5.4"),    tools=[],    middleware=[CopilotKitMiddleware()],    system_prompt="You are a helpful, concise assistant.",)

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.

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:

slot-overrides.snippet.tsx
import type {  CopilotChatAssistantMessage,  CopilotChatInput,  CopilotChatView,} from "@copilotkit/react-core/v2";declare const CustomWelcomeScreen: React.ComponentType;declare const CustomAssistantMessage: React.ComponentType;declare const CustomDisclaimer: React.ComponentType;export function ChatSlotsTeachingExtracts() {  const welcomeScreen =    CustomWelcomeScreen as unknown as typeof CopilotChatView.WelcomeScreen;

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:

slot-overrides.snippet.tsx
import type {  CopilotChatAssistantMessage,  CopilotChatInput,  CopilotChatView,} from "@copilotkit/react-core/v2";declare const CustomWelcomeScreen: React.ComponentType;declare const CustomAssistantMessage: React.ComponentType;declare const CustomDisclaimer: React.ComponentType;export function ChatSlotsTeachingExtracts() {  const welcomeScreen =    CustomWelcomeScreen as unknown as typeof CopilotChatView.WelcomeScreen;  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:

slot-overrides.snippet.tsx
import type {  CopilotChatAssistantMessage,  CopilotChatInput,  CopilotChatView,} from "@copilotkit/react-core/v2";declare const CustomWelcomeScreen: React.ComponentType;declare const CustomAssistantMessage: React.ComponentType;declare const CustomDisclaimer: React.ComponentType;export function ChatSlotsTeachingExtracts() {  const welcomeScreen =    CustomWelcomeScreen as unknown as typeof CopilotChatView.WelcomeScreen;  const messageView = {    assistantMessage:      CustomAssistantMessage as unknown as typeof CopilotChatAssistantMessage,  };  const input = {    disclaimer:      CustomDisclaimer as unknown as typeof CopilotChatInput.Disclaimer,  };

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
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
<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
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
<CopilotChat
  messageView={{
    assistantMessage: {
      toolbar: CustomToolbar,
      copyButton: CustomCopyButton,
    },
    userMessage: CustomUserMessage,
  }}
/>

Three levels deep#

Override a specific button inside the assistant message toolbar:

page.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
<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.