State Rendering
Render your agent's state with custom UI components in real-time.
What is this?#
State rendering lets you build UI that reflects your agent's state in real-time. As your agent progresses through nodes and emits state updates, your frontend renders those changes, showing progress, drafts, or intermediate results.
Free course: See this pattern built end-to-end in Build Interactive Agents with Generative UI — a free DeepLearning.AI short course taught by CopilotKit's CEO covering the full Generative UI spectrum (Controlled, Declarative, and Open-Ended).
When should I use this?#
Use state rendering when you want to:
- Show real-time progress (e.g. "Researching... 2/5 complete")
- Display drafts that update as the agent works
- Build dashboards that reflect agent state
- Render structured output outside of the chat
How it works in code#
On the frontend, subscribe to the agent's state. Each time the backend forwards a fresh value, your component re-renders with the latest partial output.
// Subscribe to BOTH state changes and run-status changes. The former // drives the per-token document rerender; the latter toggles the // "LIVE" badge when the agent starts / stops. const { agent } = useAgent({ agentId: "shared-state-streaming", updates: [UseAgentUpdate.OnStateChanged, UseAgentUpdate.OnRunStatusChanged], });On the backend, a state-streaming middleware forwards a specific tool argument straight into a state key as it's being generated, so the UI can watch the answer assemble token-by-token rather than appearing in one burst between node transitions.
import uuidfrom langchain.agents import AgentState as BaseAgentState, create_agentfrom langchain.tools import ToolRuntime, toolfrom langchain_core.messages import ToolMessagefrom langchain_openai import ChatOpenAIfrom langgraph.types import Commandfrom copilotkit import ( CopilotKitMiddleware, StateItem, StateStreamingMiddleware,)class AgentState(BaseAgentState): """Shared state. `document` is streamed token-by-token.""" document: str@tooldef write_document(document: str, runtime: ToolRuntime) -> Command: """Write a document for the user. Always call this tool when the user asks you to write or draft something of any length (an essay, poem, email, summary, etc.). The `document` argument is streamed *per token* into shared agent state under the `document` key, so the UI can render it as it is generated. """ return Command( update={ "document": document, "messages": [ ToolMessage( content="Document written to shared state.", name="write_document", id=str(uuid.uuid4()), tool_call_id=runtime.tool_call_id, ) ], } )graph = create_agent( model=ChatOpenAI(model="gpt-5.4"), tools=[write_document], middleware=[ CopilotKitMiddleware(), # Forward every token of write_document's `document` argument # straight into state["document"] while the tool call is still # streaming. Without this, `document` would only update once # the tool call completes. # # NOTE: the frontend `usePredictStateSubscription` hook indexes # the (partial-JSON-parsed) tool args by `state_key`, so the # tool's argument name MUST match `state_key` ("document") for # per-token deltas to land in `state.document`. StateStreamingMiddleware( StateItem( state_key="document", tool="write_document", tool_argument="document", ) ), ], state_schema=AgentState, system_prompt=( "You are a collaborative writing assistant. Whenever the user asks " "you to write, draft, or revise any piece of text, ALWAYS call the " "`write_document` tool with the full content as a single string in " "the `document` argument. Never paste the document into a chat " "message directly — the document belongs in shared state and the " "UI renders it live as you type." ),)