Files
copilotkit__copilotkit/docs/snippets/shared/basics/headless-ui.mdx
Maxim b039983f59 docs: extract performance tip into shared snippet
Moves the streaming performance tip into a reusable snippet and imports
it on both the root headless page and the v2 basics headless-ui snippet
(used by built-in-agent and a2a framework pages).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 20:00:02 +02:00

144 lines
4.2 KiB
Plaintext

## What is this?
A headless UI gives you full control over the chat experience — you bring your own components, layout, and styling while CopilotKit handles agent communication, message management, and streaming. This is built on top of the same primitives (`useAgent` and `useCopilotKit`) covered in Programmatic Control.
## When should I use this?
Use headless UI when the slot system isn't enough — for example, when you need a completely different layout, want to embed the chat into an existing UI, or are building a non-chat interface that still communicates with an agent.
## Implementation
<Steps>
<Step>
### Access the agent and CopilotKit
Use `useAgent` to get the agent instance (messages, state, execution status) and `useCopilotKit` to run the agent.
```tsx title="components/custom-chat.tsx"
import { useAgent } from "@copilotkit/react-core/v2";
import { useCopilotKit } from "@copilotkit/react-core/v2";
import { randomUUID } from "@copilotkit/shared/v2";
export function CustomChat() {
// [!code highlight:2]
const { agent } = useAgent();
const { copilotkit } = useCopilotKit();
return <div>{/* Your custom UI */}</div>;
}
```
</Step>
<Step>
### Display messages
The agent's messages are available via `agent.messages`. Each message has an `id`, `role` (`"user"` or `"assistant"`), and `content`.
```tsx title="components/custom-chat.tsx"
export function CustomChat() {
const { agent } = useAgent();
const { copilotkit } = useCopilotKit();
return (
<div className="flex flex-col h-full">
{/* [!code highlight:12] */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{agent.messages.map((msg) => (
<div
key={msg.id}
className={msg.role === "user" ? "ml-auto bg-blue-100 rounded-lg p-3 max-w-md" : "bg-gray-100 rounded-lg p-3 max-w-md"}
>
<p className="text-sm font-medium">{msg.role}</p>
<p>{msg.content}</p>
</div>
))}
{agent.isRunning && <div className="text-gray-400">Thinking...</div>}
</div>
</div>
);
}
```
</Step>
<Step>
### Send messages and run the agent
Add a message to the agent's conversation, then call `copilotkit.runAgent()` to trigger execution. This is the same method CopilotKit's built-in `<CopilotChat />` uses internally.
```tsx title="components/custom-chat.tsx"
import { useState, useCallback } from "react";
export function CustomChat() {
const { agent } = useAgent();
const { copilotkit } = useCopilotKit();
const [input, setInput] = useState("");
// [!code highlight:14]
const sendMessage = useCallback(async () => {
if (!input.trim()) return;
agent.addMessage({
id: randomUUID(),
role: "user",
content: input,
});
setInput("");
await copilotkit.runAgent({ agent });
}, [input, agent, copilotkit]);
return (
<div className="flex flex-col h-full">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{agent.messages.map((msg) => (
<div key={msg.id} className={msg.role === "user" ? "ml-auto bg-blue-100 rounded-lg p-3 max-w-md" : "bg-gray-100 rounded-lg p-3 max-w-md"}>
<p>{msg.content}</p>
</div>
))}
{agent.isRunning && <div className="text-gray-400">Thinking...</div>}
</div>
{/* [!code highlight:12] */}
<form
className="border-t p-4 flex gap-2"
onSubmit={(e) => { e.preventDefault(); sendMessage(); }}
>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
className="flex-1 border rounded-lg px-3 py-2"
/>
<button type="submit" disabled={agent.isRunning}>Send</button>
</form>
</div>
);
}
```
</Step>
<Step>
### Stop the agent
Use `copilotkit.stopAgent()` to cancel a running agent:
```tsx title="components/custom-chat.tsx"
const stopAgent = useCallback(() => {
// [!code highlight:1]
copilotkit.stopAgent({ agent });
}, [agent, copilotkit]);
// In your JSX:
{agent.isRunning && (
<button onClick={stopAgent} className="text-red-500">
Stop
</button>
)}
```
</Step>
</Steps>
import PerformanceTip from "@/snippets/shared/performance-tip.mdx";
<PerformanceTip components={props.components} />