---
title: State Rendering
icon: "lucide/Bot"
description: Render your agent's state with custom UI components in real-time.
---
import { Tabs, Tab } from "fumadocs-ui/components/tabs"
import RunAndConnect from "@/snippets/integrations/langgraph/run-and-connect.mdx"
import { IframeSwitcher } from "@/components/content"

<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"
/>

## 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.

## 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

## Implementation

<Steps>
  <Step>
    ### Run and connect your agent
    <RunAndConnect components={props.components} />
  </Step>
  <Step>
    ### Define your agent state

    Add properties to your agent state that you want to render in the UI.

    <Tabs groupId="language_langgraph_agent" items={['Python', 'TypeScript']} default="Python" persist>
        <Tab value="Python">
          ```python title="agent.py"
          from copilotkit import CopilotKitState

          class AgentState(CopilotKitState):
              searches: list[dict]
          ```
        </Tab>
        <Tab value="TypeScript">
          ```typescript title="agent-js/src/agent.ts"
          import { StateSchema } from "@langchain/langgraph";
          import { CopilotKitStateSchema } from "@copilotkit/sdk-js/langgraph";
          import { z } from "zod";

          export const AgentStateSchema = new StateSchema({
            searches: z.array(z.object({ query: z.string(), done: z.boolean() })).default(() => []),
            ...CopilotKitStateSchema.fields,
          });
          export type AgentState = typeof AgentStateSchema.State;
          ```
        </Tab>
    </Tabs>
  </Step>
  <Step>
    ### Emit state updates from your agent

    Use `copilotkit_emit_state` to push intermediate state to the frontend before a node finishes.

    <Tabs groupId="language_langgraph_agent" items={['Python', 'TypeScript']} default="Python" 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

        async def chat_node(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]

            response = await ChatOpenAI(model="gpt-5.4").ainvoke(
                [SystemMessage(content="You are a helpful assistant."), *state["messages"]],
                config,
            )
            return {**state, "messages": response}
        ```
      </Tab>
      <Tab value="TypeScript">
        ```typescript title="agent-js/src/agent.ts"
        import { copilotkitEmitState } from "@copilotkit/sdk-js/langgraph"; // [!code highlight]

        async function chat_node(state: AgentState, config: RunnableConfig) {
          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]
          }

          const response = await new ChatOpenAI({ model: "gpt-5.4" }).invoke(
            [new SystemMessage("You are a helpful assistant."), ...state.messages],
            config,
          );
          return { ...state, messages: response };
        }
        ```
      </Tab>
    </Tabs>
  </Step>
  <Step>
    ### Render state in the UI

    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>
      );
    }
    ```
  </Step>
  <Step>
    ### Give it a try!

    You'll see the search items appear and update in real-time as the agent progresses through each step.
  </Step>
</Steps>
