showcase/shell-docs/src/content/docs/shared-state/streaming.mdx
By default, agent state only updates between backend checkpoints, so a long-running tool call (writing a full document, drafting an email) appears to the UI as one big burst at the end. For agent-native apps, that feels broken: users expect to watch the output materialise.
State streaming forwards the value of a specific tool argument
straight into an agent state key as the argument is being generated.
The UI, subscribed via useAgent, re-renders every token.
Use state streaming whenever a tool's output is long-form text or a growing structured value and you want the user to see it assemble in real time. Common shapes:
Without streaming, the user stares at a spinner. With streaming, they see the answer grow token-by-token.
The backend pattern is always the same: map one streaming tool argument
to one shared-state key. Middleware-backed frameworks usually expose
this as a declarative mapping — for example, LangGraph Python's
StateStreamingMiddleware with StateItem(...) entries, or
copilotkitCustomizeConfig with an emitIntermediateState mapping for
LangGraph TypeScript graphs. Direct SDK adapters do the same work in
their streaming loop by parsing partial tool arguments and emitting
STATE_SNAPSHOT whenever the mapped value changes. When the LLM streams
that argument, CopilotKit writes every partial value into shared state
before the tool even finishes executing.
A few things to note:
document in this demo).write_document.document here).The UI side is identical to any other shared-state subscription:
useAgent with OnStateChanged gives you a reactive agent.state.
Add OnRunStatusChanged if you want a "LIVE" / "done" indicator.
From there, agent.state.document is just a string that grows on every
token, and agent.isRunning tells you whether to show a streaming
indicator.
<IntegrationGrid path="shared-state/streaming" exclude={["agno", "agent-spec", "spring-ai", "langroid"]} />