> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/cloudflare/agents/llms.txt
> Use this file to discover all available pages before exploring further.

# React Hooks

> React hook for connecting to agents with automatic state management

The `useAgent` hook provides a React-friendly way to connect to agents with automatic cleanup, state synchronization, and reconnection handling.

## Installation

```bash theme={null}
npm install agents partysocket
```

## Basic Usage

```tsx theme={null}
import { useAgent } from "agents/react";

function ChatRoom() {
  const agent = useAgent({
    agent: "ChatAgent",
    name: "room-123",
    onStateUpdate: (state) => {
      console.log("New state:", state);
    }
  });

  const sendMessage = async () => {
    await agent.call("sendMessage", ["Hello!"]);
  };

  return (
    <button onClick={sendMessage}>
      Send Message
    </button>
  );
}
```

## Hook Options

### UseAgentOptions

<ParamField path="agent" type="string" required>
  Name of the agent class to connect to. Automatically converted from camelCase to kebab-case for the URL.
</ParamField>

<ParamField path="name" type="string" default="default">
  Name of the specific agent instance.
</ParamField>

<ParamField path="host" type="string">
  Custom host for the WebSocket connection. Defaults to the current origin.
</ParamField>

<ParamField path="path" type="string">
  Custom path prefix for the connection URL.
</ParamField>

<ParamField path="query" type="Record<string, string | null> | (() => Promise<Record<string, string | null>>)">
  Query parameters to send with the connection. Can be a static object or an async function that returns query parameters.
</ParamField>

<ParamField path="queryDeps" type="unknown[]">
  Dependencies array for the async query function. When any dependency changes, the query function is re-executed.
</ParamField>

<ParamField path="cacheTtl" type="number" default="300000">
  Cache TTL in milliseconds for async query results. Default is 5 minutes (300000ms).
</ParamField>

<ParamField path="onStateUpdate" type="(state: State, source: 'server' | 'client') => void">
  Callback invoked when the agent's state is updated. The `source` parameter indicates whether the update came from the server or was pushed by the client.
</ParamField>

<ParamField path="onStateUpdateError" type="(error: string) => void">
  Callback invoked when a state update fails (e.g., connection is readonly).
</ParamField>

<ParamField path="onMcpUpdate" type="(mcpServers: MCPServersState) => void">
  Callback invoked when MCP server state is updated.
</ParamField>

<ParamField path="onIdentity" type="(name: string, agent: string) => void">
  Callback invoked when the server sends the agent's identity on connect. Useful when using basePath, as the actual instance name is determined server-side.
</ParamField>

<ParamField path="onIdentityChange" type="(oldName: string, newName: string, oldAgent: string, newAgent: string) => void">
  Callback invoked when identity changes on reconnect. If not provided and identity changes, a warning will be logged.
</ParamField>

<ParamField path="onOpen" type="() => void">
  Callback invoked when the WebSocket connection opens.
</ParamField>

<ParamField path="onClose" type="() => void">
  Callback invoked when the WebSocket connection closes.
</ParamField>

<ParamField path="onError" type="(error: Event) => void">
  Callback invoked when a WebSocket error occurs.
</ParamField>

<ParamField path="onMessage" type="(message: MessageEvent) => void">
  Callback invoked when a raw WebSocket message is received.
</ParamField>

## Return Value

The hook returns a `PartySocket` instance with additional agent-specific properties and methods:

<ResponseField name="agent" type="string">
  The kebab-case agent name.
</ResponseField>

<ResponseField name="name" type="string">
  The agent instance name.
</ResponseField>

<ResponseField name="identified" type="boolean">
  Whether the client has received identity from the server.
</ResponseField>

<ResponseField name="ready" type="Promise<void>">
  Promise that resolves when identity has been received from the server. Resets on connection close.
</ResponseField>

<ResponseField name="setState" type="(state: State) => void">
  Push state updates to the agent.
</ResponseField>

<ResponseField name="call" type="<T>(method: string, args?: unknown[], streamOptions?: StreamOptions) => Promise<T>">
  Call a method on the agent.
</ResponseField>

<ResponseField name="stub" type="Proxy">
  Proxy object for typed method calls.
</ResponseField>

<ResponseField name="send" type="(data: string | ArrayBuffer | Blob) => void">
  Send raw WebSocket message.
</ResponseField>

<ResponseField name="close" type="() => void">
  Close the WebSocket connection.
</ResponseField>

<ResponseField name="reconnect" type="() => void">
  Force reconnection.
</ResponseField>

## Type Safety

Pass your agent class and state type as type parameters for full type safety:

```tsx theme={null}
import type { MyAgent, MyAgentState } from "./agents/my-agent";

function MyComponent() {
  const agent = useAgent<MyAgent, MyAgentState>({
    agent: "MyAgent",
    name: "instance-1",
    onStateUpdate: (state) => {
      // state is typed as MyAgentState
      console.log(state.count);
    }
  });

  // Method calls are fully typed
  const handleClick = async () => {
    const result = await agent.stub.processData({ input: "test" });
    // result type is inferred from MyAgent.processData return type
  };

  return <button onClick={handleClick}>Process</button>;
}
```

## State Management

### Receiving State Updates

The `onStateUpdate` callback receives both the new state and its source:

```tsx theme={null}
const agent = useAgent({
  agent: "CounterAgent",
  name: "counter-1",
  onStateUpdate: (state, source) => {
    if (source === "server") {
      console.log("Server pushed state:", state);
    } else {
      console.log("Client pushed state:", state);
    }
    setLocalState(state);
  }
});
```

### Pushing State Updates

```tsx theme={null}
const incrementCounter = () => {
  agent.setState({ count: localState.count + 1 });
};
```

<Info>
  When you call `setState()`, your `onStateUpdate` callback will fire with `source: "client"` after the agent broadcasts the update.
</Info>

## Async Query Parameters

For authentication tokens or other async data, use an async query function:

```tsx theme={null}
function AuthenticatedChat({ userId }: { userId: string }) {
  const agent = useAgent({
    agent: "ChatAgent",
    name: "chat-room",
    query: async () => {
      const token = await getAuthToken();
      return { token, userId };
    },
    queryDeps: [userId],
    cacheTtl: 60 * 1000 // 1 minute
  });

  // ...
}
```

<Steps>
  <Step title="Query Execution">
    The query function is called before establishing the WebSocket connection.
  </Step>

  <Step title="Caching">
    The result is cached for the duration specified by `cacheTtl` (default: 5 minutes).
  </Step>

  <Step title="Re-execution">
    The query is re-executed when:

    * Any value in `queryDeps` changes
    * The `cacheTtl` expires
    * The component remounts
  </Step>
</Steps>

## Calling Agent Methods

### Using call()

```tsx theme={null}
const handleSubmit = async () => {
  try {
    const result = await agent.call("createPost", [title, content]);
    console.log("Post created:", result);
  } catch (error) {
    console.error("Failed to create post:", error);
  }
};
```

### Using the Stub Proxy

```tsx theme={null}
const handleSubmit = async () => {
  try {
    const result = await agent.stub.createPost(title, content);
    console.log("Post created:", result);
  } catch (error) {
    console.error("Failed to create post:", error);
  }
};
```

<Tip>
  The stub proxy provides better TypeScript inference and a more natural calling syntax.
</Tip>

### Streaming Responses

Handle streaming responses with the `onChunk`, `onDone`, and `onError` callbacks:

```tsx theme={null}
function AIChat() {
  const [output, setOutput] = useState("");
  const [isStreaming, setIsStreaming] = useState(false);
  const agent = useAgent({ agent: "AIAgent", name: "chat" });

  const handleGenerate = async () => {
    setIsStreaming(true);
    setOutput("");

    await agent.call("generateText", [prompt], {
      onChunk: (chunk) => {
        setOutput((prev) => prev + chunk);
      },
      onDone: () => {
        setIsStreaming(false);
      },
      onError: (error) => {
        setIsStreaming(false);
        console.error("Stream error:", error);
      }
    });
  };

  return (
    <div>
      <button onClick={handleGenerate} disabled={isStreaming}>
        {isStreaming ? "Generating..." : "Generate"}
      </button>
      <pre>{output}</pre>
    </div>
  );
}
```

## Lifecycle Management

### Automatic Cleanup

The hook automatically closes the WebSocket connection when the component unmounts:

```tsx theme={null}
function TemporaryConnection() {
  const agent = useAgent({
    agent: "MyAgent",
    name: "temp",
    onClose: () => {
      console.log("Connection closed");
    }
  });

  // Connection is automatically closed when component unmounts
  return <div>Connected to {agent.name}</div>;
}
```

### Manual Reconnection

Force a reconnection with the `reconnect()` method:

```tsx theme={null}
const handleReconnect = () => {
  agent.reconnect();
};
```

### Waiting for Identity

Use the `ready` promise to wait for the agent to send its identity:

```tsx theme={null}
const agent = useAgent({
  agent: "MyAgent",
  name: "instance-1"
});

useEffect(() => {
  agent.ready.then(() => {
    console.log(`Connected to ${agent.name}`);
  });
}, [agent]);
```

## Connection Events

Handle connection lifecycle events with callbacks:

```tsx theme={null}
const agent = useAgent({
  agent: "MyAgent",
  name: "my-instance",
  onOpen: () => {
    console.log("Connection opened");
  },
  onClose: () => {
    console.log("Connection closed, will auto-reconnect");
  },
  onError: (error) => {
    console.error("Connection error:", error);
  }
});
```

<Note>
  The client automatically reconnects on connection loss. You do not need to manually handle reconnection logic.
</Note>

## MCP Server Updates

Receive updates about MCP server state:

```tsx theme={null}
function MCPStatus() {
  const [mcpServers, setMcpServers] = useState<MCPServersState>({});

  const agent = useAgent({
    agent: "AssistantAgent",
    name: "session",
    onMcpUpdate: (servers) => {
      setMcpServers(servers);
    }
  });

  return (
    <div>
      {Object.entries(mcpServers).map(([id, server]) => (
        <div key={id}>
          <h3>{id}</h3>
          <p>Status: {server.connectionState}</p>
          <p>Tools: {server.tools?.map((t) => t.name).join(", ")}</p>
        </div>
      ))}
    </div>
  );
}
```

## Examples

### Real-time Counter

```tsx theme={null}
import { useAgent } from "agents/react";
import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  const agent = useAgent({
    agent: "CounterAgent",
    name: "shared-counter",
    onStateUpdate: (state) => {
      setCount(state.count);
    }
  });

  const increment = () => {
    agent.setState({ count: count + 1 });
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
```

### Authenticated Connection

```tsx theme={null}
import { useAgent } from "agents/react";
import { useAuth } from "./hooks/use-auth";

function PrivateChat() {
  const { user } = useAuth();

  const agent = useAgent({
    agent: "PrivateChatAgent",
    name: "private-room",
    query: async () => {
      const token = await getAuthToken();
      return { token, userId: user.id };
    },
    queryDeps: [user.id],
    cacheTtl: 55 * 60 * 1000 // Refresh 5 min before 1 hour expiry
  });

  // ...
}
```

### Streaming AI Response

```tsx theme={null}
import { useAgent } from "agents/react";
import { useState } from "react";

function AIAssistant() {
  const [response, setResponse] = useState("");
  const [isGenerating, setIsGenerating] = useState(false);

  const agent = useAgent({
    agent: "AIAgent",
    name: "assistant"
  });

  const handleGenerate = async (prompt: string) => {
    setIsGenerating(true);
    setResponse("");

    await agent.call("generateResponse", [prompt], {
      onChunk: (chunk) => {
        setResponse((prev) => prev + chunk);
      },
      onDone: () => {
        setIsGenerating(false);
      },
      onError: (error) => {
        setIsGenerating(false);
        console.error(error);
      }
    });
  };

  return (
    <div>
      <button
        onClick={() => handleGenerate("Hello!")}
        disabled={isGenerating}
      >
        {isGenerating ? "Generating..." : "Generate"}
      </button>
      <pre>{response}</pre>
    </div>
  );
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Type Parameters" icon="code">
    Pass your agent and state types for full type safety and autocomplete.
  </Card>

  <Card title="Handle Reconnection" icon="rotate">
    The hook automatically reconnects. Your `onStateUpdate` will fire with the latest state on reconnect.
  </Card>

  <Card title="Cache Auth Tokens" icon="key">
    Use `cacheTtl` and `queryDeps` to optimize auth token fetching.
  </Card>

  <Card title="Clean Up Side Effects" icon="broom">
    Use `useEffect` cleanup functions for any side effects triggered by state updates.
  </Card>
</CardGroup>

## See Also

<CardGroup cols={2}>
  <Card title="Client SDK Overview" icon="book" href="/client/client-sdk">
    Learn about the full client SDK capabilities
  </Card>

  <Card title="Vanilla JS Client" icon="code" href="/client/vanilla-js">
    Use AgentClient in non-React environments
  </Card>
</CardGroup>
