showcase/shell-docs/src/content/docs/prebuilt-components/chat-controls.mdx
Two common controls for the prebuilt chat are opening it from your own UI and capturing thumbs-up / thumbs-down feedback on assistant messages.
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>
You can also set the initial open state declaratively. The prebuilt surfaces
accept a defaultOpen prop:
<CopilotSidebar defaultOpen />
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:
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>.
When the slot is rendered through CopilotChatMessageView, a live assistant
message created by a direct AG-UI TEXT_MESSAGE_START can also include that
event's opaque rawEvent value. The join happens when the thumbs callback runs;
canonical messages and future run input stay unchanged. Chunk, snapshot,
persisted, legacy, and direct CopilotChatAssistantMessage paths don't provide
this callback metadata.
The button labels come from the chat labels (assistantMessageToolbarThumbsUpLabel
defaults to "Good response", assistantMessageToolbarThumbsDownLabel to
"Bad response"); override them via
chat labels.
useCopilotChatConfiguration reference: full context shape, modal state, and labels.messageView / assistantMessage slot system used above.