docs/snippets/use-agent.mdx
First, import `useAgent` from the v2 package:
```tsx title="page.tsx"
import { useAgent } from "@copilotkit/react-core/v2"; // [!code highlight]
```
Call the hook to get a reference to your agent:
```tsx title="page.tsx"
export function AgentInfo() {
const { agent } = useAgent(); // [!code highlight]
return (
<div>
<p>Agent ID: {agent.id}</p>
<p>Thread ID: {agent.threadId}</p>
<p>Status: {agent.isRunning ? "Running" : "Idle"}</p>
<p>Messages: {agent.messages.length}</p>
</div>
);
}
```
The hook will throw an error if no agent is configured, so you can safely use `agent` without null checks.
Access the agent's conversation history:
```tsx title="page.tsx"
export function MessageList() {
const { agent } = useAgent();
return (
<div>
{agent.messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong>
<span>{msg.content}</span>
</div>
))}
</div>
);
}
```
Add a loading indicator when the agent is processing:
```tsx title="page.tsx"
export function AgentStatus() {
const { agent } = useAgent();
return (
<div>
{agent.isRunning ? (
<div>
<div className="spinner" />
<span>Agent is processing...</span>
</div>
) : (
<span>Ready</span>
)}
</div>
);
}
```
Use `copilotkit.runAgent()` to trigger your agent programmatically:
```tsx title="page.tsx"
import { useAgent } from "@copilotkit/react-core/v2";
import { useCopilotKit } from "@copilotkit/react-core/v2";
import { randomUUID } from "@copilotkit/shared/v2";
export function RunAgent() {
const { agent } = useAgent();
// [!code highlight:1]
const { copilotkit } = useCopilotKit();
const handleRun = async () => {
agent.addMessage({
id: randomUUID(),
role: "user",
content: "Hello, agent!",
});
// [!code highlight:1]
await copilotkit.runAgent({ agent });
};
return <button onClick={handleRun}>Send</button>;
}
```
`copilotkit.runAgent()` orchestrates the full agent lifecycle — executing frontend tools, handling follow-up runs, and streaming results. This is the same method `<CopilotChat />` uses internally.
Agents expose their state through the agent.state property. This state is shared between your application and the agent - both can read and modify it.
Access your agent's current state:
export function StateDisplay() {
const { agent } = useAgent();
return (
<div>
<h3>Agent State</h3>
<pre>{JSON.stringify(agent.state, null, 2)}</pre>
{agent.state.user_name && <p>User: {agent.state.user_name}</p>}
{agent.state.preferences && <p>Preferences: {JSON.stringify(agent.state.preferences)}</p>}
</div>
);
}
Your component automatically re-renders when the agent's state changes.
Update state that your agent can access:
export function ThemeSelector() {
const { agent } = useAgent();
const updateTheme = (theme: string) => {
// [!code highlight:4]
agent.setState({
...agent.state,
user_theme: theme,
});
};
return (
<div>
<button onClick={() => updateTheme("dark")}>Dark Mode</button>
<button onClick={() => updateTheme("light")}>Light Mode</button>
<p>Current: {agent.state.user_theme || "default"}</p>
</div>
);
}
State updates are immediately available to your agent in its next execution.
You can subscribe to agent events using the subscribe() method. This is useful for logging, monitoring, or responding to specific agent behaviors.
import { useEffect } from "react";
import { useAgent } from "@copilotkit/react-core/v2";
import type { AgentSubscriber } from "@ag-ui/client";
export function EventLogger() {
const { agent } = useAgent();
useEffect(() => {
// [!code highlight:15]
const subscriber: AgentSubscriber = {
onCustomEvent: ({ event }) => {
console.log("Custom event:", event.name, event.value);
},
onRunStartedEvent: () => {
console.log("Agent started running");
},
onRunFinalized: () => {
console.log("Agent finished running");
},
onStateChanged: (state) => {
console.log("State changed:", state);
},
};
// [!code highlight:2]
const { unsubscribe } = agent.subscribe(subscriber);
return () => unsubscribe();
}, []);
return null;
}
The AgentSubscriber interface provides:
onCustomEvent - Custom events emitted by the agentonRunStartedEvent - Agent starts executingonRunFinalized - Agent completes executiononStateChanged - Agent's state changesonMessagesChanged - Messages are added or modifiedYou can customize how agent tool calls are displayed in your UI. First, define your tool renderers:
import { defineToolCallRenderer } from "@copilotkit/react-core/v2";
// [!code highlight:6]
export const weatherToolRender = defineToolCallRenderer({
name: "get_weather",
render: ({ args, status }) => {
return <WeatherCard location={args.location} status={status} />;
},
});
function WeatherCard({ location, status }: { location?: string; status: string }) {
return (
<div className="rounded-lg border p-6 shadow-sm">
<h3 className="text-xl font-semibold">Weather in {location}</h3>
<div className="mt-4">
<span className="text-5xl font-light">70°F</span>
</div>
{status === "executing" && <div className="spinner">Loading...</div>}
</div>
);
}
Register your tool renderers with CopilotKit:
import { CopilotKit } from "@copilotkit/react-core";
import { weatherToolRender } from "./components/weather-tool";
export default function RootLayout({ children }) {
return (
<CopilotKit
runtimeUrl="/api/copilotkit"
renderToolCalls={[weatherToolRender]}
>
{children}
</CopilotKit>
);
}
Then use useRenderToolCall to render tool calls from agent messages:
import { useAgent, useRenderToolCall } from "@copilotkit/react-core/v2";
export function MessageList() {
const { agent } = useAgent();
const renderToolCall = useRenderToolCall();
return (
<div className="messages">
{agent.messages.map((message) => (
<div key={message.id}>
{message.content && <p>{message.content}</p>}
{message.role === "assistant" && message.toolCalls?.map((toolCall) => {
const toolMessage = agent.messages.find(
(m) => m.role === "tool" && m.toolCallId === toolCall.id
);
return (
<div key={toolCall.id}>
{renderToolCall({ toolCall, toolMessage })}
</div>
);
})}
</div>
))}
</div>
);
}
Here's a full example combining all concepts into an interactive agent dashboard:
"use client";
import { useAgent } from "@copilotkit/react-core/v2";
export default function AgentDashboard() {
const { agent } = useAgent();
return (
<div className="p-8 max-w-4xl mx-auto space-y-6">
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Agent Status</h2>
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className={`w-3 h-3 rounded-full ${
agent.isRunning ? "bg-yellow-500 animate-pulse" : "bg-green-500"
}`} />
<span>{agent.isRunning ? "Running" : "Idle"}</span>
</div>
<div>Thread: {agent.threadId}</div>
<div>Messages: {agent.messages.length}</div>
</div>
</div>
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Agent State</h2>
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto">
{JSON.stringify(agent.state, null, 2)}
</pre>
</div>
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Conversation</h2>
<div className="space-y-3">
{agent.messages.map((msg) => (
<div
key={msg.id}
className={`p-3 rounded-lg ${
msg.role === "user" ? "bg-blue-50 ml-8" : "bg-gray-50 mr-8"
}`}
>
<div className="font-semibold text-sm mb-1">
{msg.role === "user" ? "You" : "Agent"}
</div>
<div>{msg.content}</div>
</div>
))}
</div>
</div>
</div>
);
}