useAgent Hook
Access and interact with your PydanticAI agent directly from React components
Overview
The useAgent hook provides direct access to your PydanticAI agent from any React component. It gives you real-time access to the agent's state, messages, execution status, and allows you to subscribe to custom events.
This enables you to build custom agent dashboards, monitoring tools, and interactive features that respond to your agent's behavior.
Navigation
This page covers everything you need to know about using useAgent with PydanticAI. Select where you'd like to get started below.
Getting started
Learn the basics of accessing your agent and displaying its properties.
Working with State
Access and update shared state between your app and agent.
Agent Events
Subscribe to agent lifecycle events and custom events.
Reference
Complete API reference documentation for useAgent.
Getting started
Let's start by building a simple component that displays agent information.
Import the hook
First, import useAgent from the v2 package:
import { useAgent } from "@copilotkit/react-core/v2"; Access your agent
Call the hook to get a reference to your agent:
export function AgentInfo() {
const { agent } = useAgent();
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.
Display messages
Access the agent's conversation history:
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>
);
}Show running status
Add a loading indicator when the agent is processing:
export function AgentStatus() {
const { agent } = useAgent();
return (
<div>
{agent.isRunning ? (
<div>
<div className="spinner" />
<span>Agent is processing...</span>
</div>
) : (
<span>Ready</span>
)}
</div>
);
}Working with State
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.
Reading State
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>
{/* Access specific properties */}
{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.
Updating State
Update state that your agent can access:
export function ThemeSelector() {
const { agent } = useAgent();
const updateTheme = (theme: string) => {
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.
Subscribing to Agent Events
You can subscribe to agent events using the subscribe() method. This is useful for logging, monitoring, or responding to specific agent behaviors.
Basic Event Subscription
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(() => {
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);
},
};
const { unsubscribe } = agent.subscribe(subscriber);
return () => unsubscribe();
}, []);
return null;
}Available Events
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 modified
Rendering Tool Calls
You can customize how agent tool calls are displayed in your UI. First, define your tool renderers:
import { defineToolCallRenderer } from "@copilotkit/react-core/v2";
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}>
{/* Display message content */}
{message.content && <p>{message.content}</p>}
{/* Render tool calls if present */}
{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>
);
}Building a Complete Dashboard
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">
{/* Status */}
<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>
{/* State */}
<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>
{/* Messages */}
<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>
);
}Model-Specific State
If your PydanticAI agent uses Pydantic models for state, you can access typed properties:
export function ModelStatus() {
const { agent } = useAgent();
const validationStatus = agent.state.validation_status;
return (
<div>
{validationStatus === "pending" && (
<div className="alert">Agent is validating input...</div>
)}
{validationStatus === "complete" && (
<div className="alert">Validation complete!</div>
)}
</div>
);
}See Also
- Shared State - Deep dive into state management
- Agent App Context - Pass context to your agent
- useAgent API Reference - Complete API documentation
