docs/content/docs/integrations/langgraph/programmatic-control.mdx
import { MessageCircle, Database, Zap, AlertCircle, BookOpen, Play, Settings, } from "lucide-react"
The useAgent hook provides direct access to your LangChain 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 like interrupts.
This enables you to build custom agent dashboards, monitoring tools, and interactive features that respond to your agent's behavior.
This page covers everything you need to know about using useAgent with LangChain. Select where you'd like to get started below.
Let's start by building a simple component that displays agent information.
import UseAgentSnippet from "@/snippets/use-agent.mdx";
<UseAgentSnippet components={props.components} />If your LangChain agent tracks which node it's in, you can show contextual UI:
export function NodeStatus() {
const { agent } = useAgent();
// [!code highlight:1]
const currentNode = agent.state.currentNode;
return (
<div>
{currentNode === "research_node" && (
<div className="alert">Agent is researching your query...</div>
)}
{currentNode === "summarize_node" && (
<div className="alert">Agent is summarizing findings...</div>
)}
</div>
);
}
Use copilotkit.runAgent() to trigger your agent from any component — no chat UI required. This is the same method CopilotKit's built-in <CopilotChat /> uses internally.
import { useAgent } from "@copilotkit/react-core/v2";
import { useCopilotKit } from "@copilotkit/react-core/v2";
import { randomUUID } from "@copilotkit/shared/v2";
export function AgentTrigger() {
const { agent } = useAgent();
// [!code highlight:1]
const { copilotkit } = useCopilotKit();
const handleRun = async () => {
// Add a user message to the agent's conversation
agent.addMessage({
id: randomUUID(),
role: "user",
content: "Summarize the latest sales data",
});
// [!code highlight:2]
// Run the agent — handles tool execution, follow-ups, and streaming
await copilotkit.runAgent({ agent });
};
return <button onClick={handleRun}>Run Agent</button>;
}
copilotkit.runAgent() vs agent.runAgent()Both methods trigger the agent, but they operate at different levels:
copilotkit.runAgent({ agent }) — The recommended approach. Orchestrates the full agent lifecycle: executes frontend tools, handles follow-up runs when tools request them, and manages errors through the subscriber system.agent.runAgent() — Low-level method on the agent instance. Sends the request to the runtime but does not execute frontend tools or handle follow-ups. Use this only when you need direct control over the agent execution (e.g., resuming from an interrupt with forwardedProps).You can stop a running agent using copilotkit.stopAgent():
const handleStop = () => {
copilotkit.stopAgent({ agent });
};
LangGraph's interrupt() function emits custom events that you can capture and respond to.
import { useEffect } from "react";
import { useAgent } from "@copilotkit/react-core/v2";
import type { AgentSubscriber } from "@ag-ui/client";
export function InterruptHandler() {
const { agent } = useAgent();
useEffect(() => {
const subscriber: AgentSubscriber = {
// [!code highlight:12]
onCustomEvent: ({ event }) => {
if (event.name === "on_interrupt") {
// LangGraph interrupt() was called
const response = prompt(event.value);
if (response) {
// Resume the agent with the user's response
agent.runAgent({
forwardedProps: {
command: { resume: response },
},
});
}
}
},
};
const { unsubscribe } = agent.subscribe(subscriber);
return () => unsubscribe();
}, []);
return null;
}
For a more sophisticated UI, you can render a custom component:
import { useEffect, useState } from "react";
import { useAgent } from "@copilotkit/react-core/v2";
import type { AgentSubscriber } from "@ag-ui/client";
export function CustomInterruptHandler() {
const { agent } = useAgent();
const [interrupt, setInterrupt] = useState<{ message: string } | null>(null);
useEffect(() => {
const subscriber: AgentSubscriber = {
onCustomEvent: ({ event }) => {
// [!code highlight:3]
if (event.name === "on_interrupt") {
setInterrupt({ message: event.value });
}
},
};
const { unsubscribe } = agent.subscribe(subscriber);
return () => unsubscribe();
}, []);
const handleResponse = (response: string) => {
// [!code highlight:5]
agent.runAgent({
forwardedProps: {
command: { resume: response },
},
});
setInterrupt(null);
};
if (!interrupt) return null;
return (
<div className="interrupt-modal">
<h3>Agent Needs Your Input</h3>
<p>{interrupt.message}</p>
<form onSubmit={(e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
handleResponse(formData.get("response") as string);
}}>
<input type="text" name="response" placeholder="Your response" />
<button type="submit">Submit</button>
</form>
</div>
);
}
For a more declarative approach, see useInterrupt.