showcase/shell-docs/src/content/docs/reference/v2/hooks/useAgent.mdx
useAgent is a React hook that returns an AG-UI AbstractAgent instance. The hook subscribes to agent state changes and triggers re-renders when the agent's state, messages, or execution status changes.
Throws error if no agent is configured with the specified agentId.
import { useAgent } from "@copilotkit/react-core/v2";
function useAgent(options?: UseAgentProps): { agent: AbstractAgent }
Pass an empty array `[]` to prevent automatic re-renders.
The default is resolved as a cascade: `throttleMs ?? provider.defaultThrottleMs ?? 0`. When you omit `throttleMs`, the hook inherits `defaultThrottleMs` from the enclosing `CopilotKitProvider`; when neither is set, the floor is `0` (no throttling). Passing `throttleMs={0}` explicitly disables throttling even when the provider specifies a non-zero `defaultThrottleMs`.
Uses a **leading+trailing** pattern:
- First update fires immediately (no delay for the initial token)
- Subsequent updates within the window are coalesced
- A trailing timer ensures the final state is always rendered
Only affects `OnMessagesChanged` — `OnStateChanged` and `OnRunStatusChanged` always fire immediately. Has no effect if `updates` does not include `OnMessagesChanged`.
Must be a non-negative finite number. Invalid values fall back to `0` with a `console.error`.
### Core Properties
<PropertyReference name="agentId" type="string | undefined">
Unique identifier for the agent instance.
</PropertyReference>
<PropertyReference name="description" type="string">
Human-readable description of the agent's purpose.
</PropertyReference>
<PropertyReference name="threadId" type="string">
Unique identifier for the current conversation thread.
</PropertyReference>
<PropertyReference name="messages" type="Message[]">
Array of conversation messages. Each message contains:
- `id: string` - Unique message identifier
- `role: "user" | "assistant" | "system"` - Message role
- `content: string` - Message content
</PropertyReference>
<PropertyReference name="state" type="any">
Shared state object synchronized between application and agent. Both can read and modify this state.
</PropertyReference>
<PropertyReference name="isRunning" type="boolean">
Indicates whether the agent is currently executing.
</PropertyReference>
<PropertyReference name="capabilities" type="AgentCapabilities | undefined">
The agent's declared capabilities (tools, streaming, multi-agent, etc.), populated from the runtime `/info` response. See [`useCapabilities`](/reference/v2/hooks/useCapabilities) for a convenience hook that reads this directly.
</PropertyReference>
### Methods
<PropertyReference name="runAgent" type="(options?: RunAgentOptions) => Promise<void>">
Manually triggers agent execution.
**Parameters:**
- `options.forwardedProps?: any` - Data to pass to the agent execution context
**Example:**
```tsx
await agent.runAgent({
forwardedProps: {
command: { resume: "user response" }
}
});
```
</PropertyReference>
<PropertyReference name="setState" type="(newState: any) => void">
Updates the shared state. Changes are immediately available to both application and agent.
**Example:**
```tsx
agent.setState({
...agent.state,
theme: "dark"
});
```
</PropertyReference>
<PropertyReference name="subscribe" type="(subscriber: AgentSubscriber) => { unsubscribe: () => void }">
Subscribes to agent events. Returns cleanup function.
**Subscriber Events:**
- `onCustomEvent?: ({ event: { name: string, value: any } }) => void` - Custom events
- `onRunStartedEvent?: () => void` - Agent execution starts
- `onRunFinalized?: () => void` - Agent execution completes
- `onStateChanged?: (state: any) => void` - State changes
- `onMessagesChanged?: (messages: Message[]) => void` - Messages added/modified
</PropertyReference>
<PropertyReference name="addMessage" type="(message: Message) => void">
Adds a single message to the conversation and notifies subscribers.
</PropertyReference>
<PropertyReference name="addMessages" type="(messages: Message[]) => void">
Adds multiple messages to the conversation and notifies subscribers once.
</PropertyReference>
<PropertyReference name="setMessages" type="(messages: Message[]) => void">
Replaces the entire message history with a new array of messages.
</PropertyReference>
<PropertyReference name="abortRun" type="() => void">
Aborts the currently running agent execution.
</PropertyReference>
<PropertyReference name="clone" type="() => AbstractAgent">
Creates a deep copy of the agent with cloned messages, state, and configuration.
</PropertyReference>
import { useAgent } from "@copilotkit/react-core/v2";
function AgentStatus() {
const { agent } = useAgent();
return (
<div>
<div>Agent: {agent.agentId}</div>
<div>Messages: {agent.messages.length}</div>
<div>Running: {agent.isRunning ? "Yes" : "No"}</div>
</div>
);
}
import { useAgent } from "@copilotkit/react-core/v2";
function StateController() {
const { agent } = useAgent();
return (
<div>
<pre>{JSON.stringify(agent.state, null, 2)}</pre>
<button onClick={() => agent.setState({ ...agent.state, count: 1 })}>
Update State
</button>
</div>
);
}
import { useEffect } from "react";
import { useAgent } from "@copilotkit/react-core/v2";
function EventListener() {
const { agent } = useAgent();
useEffect(() => {
const { unsubscribe } = agent.subscribe({
onRunStartedEvent: () => console.log("Started"),
onRunFinalized: () => console.log("Finished"),
});
return unsubscribe;
}, []);
return null;
}
import { useAgent } from "@copilotkit/react-core/v2";
function MultiAgentView() {
const { agent: primary } = useAgent({ agentId: "primary" });
const { agent: support } = useAgent({ agentId: "support" });
return (
<div>
<div>Primary: {primary.messages.length} messages</div>
<div>Support: {support.messages.length} messages</div>
</div>
);
}
import { useAgent, UseAgentUpdate } from "@copilotkit/react-core/v2";
// Only re-render when messages change
function MessageCount() {
const { agent } = useAgent({
updates: [UseAgentUpdate.OnMessagesChanged]
});
return <div>Messages: {agent.messages.length}</div>;
}
import { useAgent } from "@copilotkit/react-core/v2";
// During streaming, coalesce rapid token updates into ~10 renders/sec
function StreamingChat() {
const { agent } = useAgent({ throttleMs: 100 });
return (
<div>
{agent.messages.map((msg) => (
<div key={msg.id}>{msg.content}</div>
))}
</div>
);
}
CopilotKit provides throttle controls at two independent layers. They are additive — you can use one or both depending on your architecture:
| Layer | Option | Where | What it throttles |
|---|---|---|---|
| React hook | useAgent({ throttleMs }) | Per-component | React re-renders triggered by OnMessagesChanged |
| AG-UI middleware | EventThrottleMiddleware | Per-agent (all subscribers) | AG-UI stream events at the source — batches and coalesces rapid chunk events before they reach subscribers |
When to use which:
throttleMs on useAgent — best for headless UIs where you control the React component but not the agent creation. Simple, one-line fix.EventThrottleMiddleware — best when you control agent instantiation and want to throttle for ALL consumers (not just one React component). Supports both time-based (intervalMs) and character-count-based (minChunkSize) thresholds. Coalesces consecutive chunk events (text, tool call, reasoning) with the same ID into a single event with merged deltas.When creating an agent directly (e.g., HttpAgent), you can add EventThrottleMiddleware to throttle stream events:
import { HttpAgent } from "@ag-ui/client";
import { EventThrottleMiddleware } from "@ag-ui/event-throttle-middleware";
const agent = new HttpAgent({
url: "https://my-agent.example.com",
});
agent.use(
new EventThrottleMiddleware({
intervalMs: 16, // ~60fps cap on notifications
minChunkSize: 20, // also fire when 20+ new chars accumulate
}),
);
When both intervalMs and minChunkSize are set, a notification fires when either threshold is hit first — whichever comes sooner. This gives you both a minimum visual update granularity (chunk size) and a maximum latency cap (time).
Bufferable events (text chunks, tool call chunks, state snapshots) are batched and coalesced, while lifecycle events (RUN_STARTED, TOOL_CALL_START, etc.) flush the buffer and pass through immediately.
Throttling reduces how often your component re-renders, but each re-render still runs .map() over all messages. For best performance, wrap your message component in React.memo so only the actively-streaming message re-renders:
const MessageBubble = React.memo(function MessageBubble({ message }) {
return <div>{message.content}</div>;
}, (prev, next) => {
return prev.message.id === next.message.id
&& prev.message.content === next.message.content;
});
This is what CopilotKit's built-in CopilotChat component does internally — it wraps every message type in React.memo with deep content comparators.
updates parameter)throttleMs > 0, message-change re-renders use a leading+trailing throttle — the first token renders instantly, rapid mid-stream updates are coalesced, and the final state is always rendered after the window expires. State and run-status changes are never throttled.agentIdsetState() are immediately available to both app and agentuseCapabilities -- convenience hook for reading agent capabilities