showcase/shell-docs/src/content/docs/integrations/deepagents/generative-ui/state-rendering.mdx
<IframeSwitcher id="agent-state-example" exampleUrl="https://feature-viewer.copilotkit.ai/langgraph/feature/agentic_generative_ui?sidebar=false&chatDefaultOpen=false" codeUrl="https://feature-viewer.copilotkit.ai/langgraph/feature/agentic_generative_ui?view=code&sidebar=false&codeLayout=tabs" exampleLabel="Demo" codeLabel="Code" height="700px" />
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.
Use state rendering when you want to:
Add properties to your agent state that you want to render in the UI.
<Tabs groupId="agent_language" items={['Python', 'TypeScript']} persist>
<Tab value="Python">
```python title="agent.py"
from copilotkit import CopilotKitState
class AgentState(CopilotKitState):
searches: list[dict]
```
</Tab>
<Tab value="TypeScript">
```ts title="agent.ts"
import { createMiddleware } from "langchain";
import { copilotkitMiddleware } from "@copilotkit/sdk-js/langgraph"; // [!code highlight]
import { z } from "zod";
export const searchesStateMiddleware = createMiddleware({
name: "AgentState",
stateSchema: z.object({
searches: z.array(z.object({
query: z.string(),
done: z.boolean(),
})).default([]),
}),
});
// Compose with copilotkitMiddleware when constructing the agent:
// createDeepAgent({ middleware: [searchesStateMiddleware, copilotkitMiddleware], ... })
```
</Tab>
</Tabs>
Use `copilotkit_emit_state` to push intermediate state to the frontend before a node finishes.
<Tabs groupId="agent_language" items={['Python', 'TypeScript']} persist>
<Tab value="Python">
```python title="agent.py"
import asyncio
from copilotkit.langgraph import copilotkit_emit_state # [!code highlight]
from langchain_core.runnables import RunnableConfig
# Inside a custom tool or middleware hook where you have access to state and config:
async def emit_research_progress(state: AgentState, config: RunnableConfig):
state["searches"] = [
{"query": "Initial research", "done": False},
{"query": "Retrieving sources", "done": False},
{"query": "Forming an answer", "done": False},
]
await copilotkit_emit_state(config, state) # [!code highlight]
for search in state["searches"]:
await asyncio.sleep(1)
search["done"] = True
await copilotkit_emit_state(config, state) # [!code highlight]
```
</Tab>
<Tab value="TypeScript">
```ts title="agent.ts"
import { copilotkitEmitState } from "@copilotkit/sdk-js/langgraph"; // [!code highlight]
// Inside a custom tool or middleware hook where you have config/state:
async function emitResearchProgress(state: any, config: any) {
state.searches = [
{ query: "Initial research", done: false },
{ query: "Retrieving sources", done: false },
{ query: "Forming an answer", done: false },
];
await copilotkitEmitState(config, state); // [!code highlight]
for (const search of state.searches) {
await new Promise((resolve) => setTimeout(resolve, 1000));
search.done = true;
await copilotkitEmitState(config, state); // [!code highlight]
}
}
```
</Tab>
</Tabs>
Use the `useAgent` hook to access agent state anywhere in your app. You can render it in the chat, in dashboards, sidebars, or custom layouts.
```tsx title="app/page.tsx"
import { useAgent } from "@copilotkit/react-core/v2"; // [!code highlight]
function YourMainContent() {
// [!code highlight:3]
const { agent } = useAgent({
agentId: "sample_agent",
});
const searches = agent.state.searches as { query: string; done: boolean }[] ?? [];
return (
<div>
{searches.map((search, index) => (
<div key={index}>
{search.done ? "✅" : "⏳"} {search.query}
</div>
))}
</div>
);
}
```
You'll see the search items appear and update in real-time as the agent progresses through each step.