Back to Copilotkit

Slots

docs/content/docs/integrations/langgraph/custom-look-and-feel/slots.mdx

1.57.08.4 KB
Original Source

What is this?

Every CopilotKit chat component is built from composable slots — named sub-components that 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.

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.

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

export function Chat() {
  return (
    <CopilotChat
      // [!code highlight:2]
      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.

tsx
<CopilotChat
  // [!code highlight:4]
  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.

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

// [!code highlight:8]
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 (
    // [!code highlight:1]
    <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:

tsx
<CopilotChat
  // [!code highlight:7]
  messageView={{
    assistantMessage: {
      toolbar: CustomToolbar,
      copyButton: CustomCopyButton,
    },
    userMessage: CustomUserMessage,
  }}
/>

Three levels deep

Override a specific button inside the assistant message toolbar:

tsx
<CopilotChat
  messageView={{
    // [!code highlight:5]
    assistantMessage: {
      copyButton: ({ onClick }) => (
        <button onClick={onClick}>Copy</button>
      ),
    },
  }}
/>

Input sub-slots

tsx
<CopilotChat
  input={{
    // [!code highlight:2]
    textArea: CustomTextArea,
    sendButton: CustomSendButton,
  }}
/>

Scroll view sub-slots

tsx
<CopilotChat
  scrollView={{
    // [!code highlight:2]
    feather: CustomFeather,
    scrollToBottomButton: CustomScrollButton,
  }}
/>

Suggestion view sub-slots

tsx
<CopilotChat
  suggestionView={{
    // [!code highlight:2]
    suggestion: CustomSuggestionPill,
    container: CustomSuggestionContainer,
  }}
/>

Children Render Function

For complete layout control, use the children render function pattern. This gives you pre-built slot elements that you can arrange however you want.

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

export function Chat() {
  return (
    <CopilotChat>
      {({ messageView, input, scrollView, suggestionView }) => (
        <div className="flex flex-col h-full">
          <header className="p-4 border-b font-semibold">My Agent</header>
          {scrollView}
          <div className="border-t p-4">{input}</div>
        </div>
      )}
    </CopilotChat>
  );
}

Labels

Customize any text string in the UI via the labels prop. This does not use the slot system — it's a separate convenience prop on CopilotChat, CopilotSidebar, and CopilotPopup.

tsx
<CopilotChat
  // [!code highlight:5]
  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.

messageView sub-slots

Available via messageView={{ ... }}:

SlotDescription
assistantMessageRenders assistant responses. Has its own sub-slots (see below).
userMessageRenders user messages. Has its own sub-slots (see below).
reasoningMessageRenders model reasoning/thinking steps. Has its own sub-slots (see below).
cursorThe streaming cursor indicator shown while the agent is responding.

assistantMessage sub-slots

Available via messageView={{ assistantMessage: { ... } }}:

SlotDescription
markdownRendererThe markdown rendering component.
toolbarThe action toolbar below messages.
copyButtonCopy message button.
thumbsUpButtonThumbs up feedback button.
thumbsDownButtonThumbs down feedback button.
readAloudButtonRead aloud button.
regenerateButtonRegenerate response button.
toolCallsViewTool call visualization.

userMessage sub-slots

Available via messageView={{ userMessage: { ... } }}:

SlotDescription
messageRendererThe text rendering component for user messages.
toolbarThe action toolbar on hover.
copyButtonCopy message button.
editButtonEdit message button.
branchNavigationNavigation between message branches (after editing).

reasoningMessage sub-slots

Available via messageView={{ reasoningMessage: { ... } }}:

SlotDescription
headerThe collapsible header (click to expand/collapse).
contentViewThe reasoning content area.
toggleThe expand/collapse toggle wrapper.

input sub-slots

Available via input={{ ... }}:

SlotDescription
textAreaThe text input element.
sendButtonThe send/submit button.
addMenuButtonThe attachment/tools menu button.
startTranscribeButtonButton to start voice transcription.
cancelTranscribeButtonButton to cancel transcription.
finishTranscribeButtonButton to finish transcription.
audioRecorderThe audio recorder component.
disclaimerThe disclaimer text below the input.

scrollView sub-slots

Available via scrollView={{ ... }}:

SlotDescription
featherThe gradient overlay at the bottom of the scroll area.
scrollToBottomButtonThe button that appears when scrolled up.

suggestionView sub-slots

Available via suggestionView={{ ... }}:

SlotDescription
suggestionIndividual suggestion pill/button.
containerThe container wrapping all suggestion pills.

welcomeScreen sub-slots

Available via welcomeScreen={{ ... }}:

SlotDescription
welcomeMessageThe welcome text shown on the empty state.

header sub-slots (Sidebar/Popup only)

Available via header={{ ... }}:

SlotDescription
titleContentThe title text in the header.
closeButtonThe close/minimize button.

toggleButton sub-slots (Sidebar/Popup only)

Available via toggleButton={{ ... }}:

SlotDescription
openIconIcon shown when the chat is closed.
closeIconIcon shown when the chat is open.