---
title: "State Rendering"
icon: "lucide/Bot"
description: Render the state of your agent with custom UI components.
---

import { Accordions, Accordion } from "fumadocs-ui/components/accordion";

<video
  src="https://cdn.copilotkit.ai/docs/copilotkit/images/coagents/agentic-generative-ui.mp4"
  className="rounded-lg shadow-xl"
  loop
  playsInline
  controls
  autoPlay
  muted
/>
<Callout>
  This video demonstrates the [implementation](#implementation) section applied
  to our [coagents starter
  project](https://github.com/CopilotKit/CopilotKit/tree/main/examples/coagents-starter-crewai-flows).
</Callout>

## What is this?

All CrewAI Flow agents are stateful. This means that as your agent progresses through nodes, a state object is passed between them perserving
the overall state of a session. CopilotKit allows you to render this state in your application with custom UI components, which we call **Agentic Generative UI**.

## When should I use this?

Rendering the state of your agent in the UI is useful when you want to provide the user with feedback about the overall state of a session. A great example of this
is a situation where a user and an agent are working together to solve a problem. The agent can store a draft in its state which is then rendered in the UI.

## Implementation

<Steps>
  <Step>
    ### Run and Connect your CrewAI Flow to CopilotKit
    First, you'll need to make sure you have a running CrewAI Flow. If you haven't already done this, you can follow the [getting started guide](/crewai-flows/quickstart)

    This guide uses the [CoAgents starter repo](https://github.com/CopilotKit/CopilotKit/tree/main/examples/coagents-starter-crewai-flows) as its starting point.

  </Step>
  <Step>
    ### Define your agent state
    If you're not familiar with CrewAI, your flows are stateful. As you progress through function, a state object is updated between them. CopilotKit
    allows you to easily render this state in your application.

    For the sake of this guide, let's say our state looks like this in our agent.

    <Tabs groupId="language_crewai-flows_agent" items={['Python']} default="Python" persist>
        <Tab value="Python">
          ```python title="agent.py"
          # ...
          from copilotkit.crewai import CopilotKitState # extends MessagesState
          # ...

          # This is the state of the agent.
          # It inherits from the CopilotKitState properties from CopilotKit.
          class AgentState(CopilotKitState):
              searches: list[dict]
          ```
        </Tab>
    </Tabs>

  </Step>
  <Step>
    ### Simulate state updates
    Next, let's write some logic into our agent that will simulate state updates occurring.

    <Tabs groupId="language_crewai-flows_agent" items={['Python']} default="Python" persist>
      <Tab value="Python">
        ```python title="agent.py"
        from crewai.flow.flow import start
        from litellm import completion
        from copilotkit.crewai import copilotkit_stream, CopilotKitState, copilotkit_emit_state
        import asyncio
        from typing import TypedDict

        class Searches(TypedDict):
            query: str
            done: bool

        class AgentState(CopilotKitState):
            searches: list[Searches] = [] # [!code highlight]

        @start
        async def chat(self):
            self.state.searches = [
                {"query": "Initial research", "done": False},
                {"query": "Retrieving sources", "done": False},
                {"query": "Forming an answer", "done": False},
            ]
            await copilotkit_emit_state(self.state)

            # Simulate state updates # [!code highlight:4]
            for search in self.state.searches:
                await asyncio.sleep(1)
                search["done"] = True
                await copilotkit_emit_state(self.state)

            # Run the model to generate a response
            response = await copilotkit_stream(
                completion(
                    model="openai/gpt-5.4",
                    messages=[
                        {"role": "system", "content": "You are a helpful assistant."},
                        *self.state.get("messages", [])
                    ],
                    stream=True
                )
            )
        ```
      </Tab>
    </Tabs>

  </Step>
  <Step>
    ### Render state of the agent in the chat
    Now we can utilize `useAgent` with a `render` function to render the state of our agent **in the chat**.

    ```tsx title="app/page.tsx"
    // ...
    import { useAgent } from "@copilotkit/react-core/v2";
    // ...

    // Define the state of the agent, should match the state of the agent in your Flow.
    type AgentState = {
      searches: {
        query: string;
        done: boolean;
      }[];
    };

    function YourMainContent() {
      // ...

      // [!code highlight:13]
      // styles omitted for brevity
      useAgent({
        agentId: "sample_agent",
        render: ({ state }) => (
          <div>
            {state.searches?.map((search, index) => (
              <div key={index}>
                {search.done ? "✅" : "❌"} {search.query}{search.done ? "" : "..."}
              </div>
            ))}
          </div>
        ),
      });

      // ...

      return <div>...</div>;
    }
    ```

  </Step>
  <Step>
    ### Render state outside of the chat
    You can also render the state of your agent **outside of the chat**. This is useful when you want to render the state of your agent anywhere
    other than the chat.

    ```tsx title="app/page.tsx"
    import { useAgent } from "@copilotkit/react-core/v2"; // [!code highlight]
    // ...

    // Define the state of the agent, should match the state of the agent in your Flow.
    type AgentState = {
      searches: {
        query: string;
        done: boolean;
      }[];
    };

    function YourMainContent() {
      // ...

      // [!code highlight:3]
      const { agent } = useAgent({
        agentId: "sample_agent",
      })

      // ...

      return (
        <div>
          {/* ... */}
          <div className="flex flex-col gap-2 mt-4">
            {/* [!code highlight:5] */}
            {agent.state?.searches?.map((search, index) => (
              <div key={index} className="flex flex-row">
                {search.done ? "✅" : "❌"} {search.query}
              </div>
            ))}
          </div>
        </div>
      )
    }
    ```

  </Step>
  <Step>
    ### Give it a try!

    You've now created a component that will render the agent's state in the chat.

  </Step>
</Steps>
