CopilotKit

State Rendering

Render your agent's state with custom UI components in real-time.


"""LangGraph agent backing the State Streaming demo.Demonstrates per-token state-delta streaming. The agent writes a long`document` string into shared agent state via a `write_document` tool;`StateStreamingMiddleware(StateItem(...))` tells CopilotKit to forward*every token* of the tool's `content` argument directly into the`document` state key as it is generated. The UI (useAgent) sees`state.document` grow token-by-token, without waiting for the tool callto finish.This is the canonical per-token state-streaming pattern:docs.copilotkit.ai/integrations/langgraph/shared-state/predictive-state-updates"""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."    ),)

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.

page.tsx
  // 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.

shared_state_streaming.py
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."    ),)