Open, close, and feedback

Open and close the prebuilt chat from your UI, and capture assistant message feedback.


Two common controls for the prebuilt chat are opening it from your own UI and capturing thumbs-up / thumbs-down feedback on assistant messages.

Programmatically open or close the chat#

The modal/open state for <CopilotPopup> and <CopilotSidebar> lives in the chat configuration context. Read it with useCopilotChatConfiguration() and call setModalOpen(true | false) from anywhere inside the provider:

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

function OpenChatButton() {
  const config = useCopilotChatConfiguration();

  // setModalOpen is only present when a provider in the tree owns modal state
  // (the prebuilt CopilotPopup / CopilotSidebar create it for you).
  if (!config?.setModalOpen) return null;

  return (
    <button onClick={() => config.setModalOpen(true)}>
      Ask the assistant
    </button>
  );
}

To toggle, read config.isModalOpen and flip it:

<button onClick={() => config.setModalOpen(!config.isModalOpen)}>
  {config.isModalOpen ? "Close chat" : "Open chat"}
</button>

setModalOpen / isModalOpen are only defined when a provider in the tree owns modal state. The prebuilt <CopilotPopup> and <CopilotSidebar> create it automatically. If you compose chat yourself, wrap the relevant subtree in <CopilotChatConfigurationProvider isModalDefaultOpen={false}> so the modal state exists. See the useCopilotChatConfiguration reference.

You can also set the initial open state declaratively. The prebuilt surfaces accept a defaultOpen prop:

<CopilotSidebar defaultOpen />

Capture message feedback (thumbs up / down)#

The assistant-message toolbar can show thumbs-up and thumbs-down buttons. In v2 you opt in by passing onThumbsUp / onThumbsDown to the assistant message slot. The buttons only render when a handler is provided: slot**. The buttons only render when a handler is provided:

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

<CopilotChat
  messageView={{
    assistantMessage: {
      onThumbsUp: (message) => {
        analytics.track("feedback", { messageId: message.id, value: "up" });
      },
      onThumbsDown: (message) => {
        analytics.track("feedback", { messageId: message.id, value: "down" });
      },
    },
  }}
/>;

Each handler receives the assistant message, so you can record the feedback against the specific response (message.id). The same messageView slot works on <CopilotPopup> and <CopilotSidebar> since they wrap <CopilotChat>.

The button labels come from the chat labels (assistantMessageToolbarThumbsUpLabel defaults to "Good response", assistantMessageToolbarThumbsDownLabel to "Bad response"); override them via chat labels.

Building a fully custom message component instead of using the slot? The underlying CopilotChatAssistantMessage exposes the same onThumbsUp / onThumbsDown props directly.