import { useAgent } from "agents/react"; import { useState } from "react"; import { Button, Input, Surface, Empty, Text } from "@cloudflare/kumo"; import { DemoWrapper } from "../../layout"; import { LogPanel, ConnectionStatus, CodeExplanation, type CodeSection } from "../../components"; import { useLogs, useUserId, useToast } from "../../hooks"; import type { ConnectionsAgent, ConnectionsAgentState } from "./connections-agent"; const codeSections: CodeSection[] = [ { title: "Lifecycle hooks for connections", description: "Override onConnect and onClose to react when clients join or leave. Use this.getConnections() to see all active WebSocket connections.", code: `import { Agent, type Connection } from "agents"; class ConnectionsAgent extends Agent { onConnect(connection: Connection) { const count = [...this.getConnections()].length; this.broadcast(JSON.stringify({ type: "connection_count", count, })); } onClose(connection: Connection) { const count = [...this.getConnections()].length; this.broadcast(JSON.stringify({ type: "connection_count", count, })); } }` }, { title: "Broadcast to all clients", description: "Call this.broadcast() to send a message to every connected WebSocket client at once. Pass an array of connection IDs as the second argument to exclude specific clients — useful for avoiding echoing a message back to the sender.", code: `import { Agent, callable, getCurrentAgent } from "agents"; @callable() broadcastMessage(message: string) { // Send to everyone this.broadcast(JSON.stringify({ type: "broadcast", message, })); } // Exclude the sender @callable() broadcastToOthers(message: string) { const { connection } = getCurrentAgent(); this.broadcast( JSON.stringify({ type: "broadcast", message }), [connection.id] // exclude this connection ); }` } ]; export function ConnectionsDemo() { const userId = useUserId(); const { logs, addLog, clearLogs } = useLogs(); const { toast } = useToast(); const [connectionCount, setConnectionCount] = useState(0); const [broadcastMessage, setBroadcastMessage] = useState( "Hello from the playground!" ); const [receivedMessages, setReceivedMessages] = useState< Array<{ message: string; timestamp: number }> >([]); const agent = useAgent({ agent: "connections-agent", name: `connections-demo-${userId}`, onOpen: () => { addLog("info", "connected"); refreshConnectionCount(); }, onClose: () => addLog("info", "disconnected"), onError: () => addLog("error", "error", "Connection error"), onMessage: (event: MessageEvent) => { try { const data = JSON.parse(event.data as string); if (data.type === "connection_count") { setConnectionCount(data.count); addLog("in", "connection_count", data.count); } else if (data.type === "broadcast") { addLog("in", "broadcast", data.message); setReceivedMessages((prev) => [ ...prev, { message: data.message, timestamp: data.timestamp } ].slice(-10) ); } } catch { // Not JSON } } }); const refreshConnectionCount = async () => { try { const count = await agent.call("getConnectionCount"); setConnectionCount(count); } catch { // Ignore } }; const handleBroadcast = async () => { if (!broadcastMessage.trim()) return; addLog("out", "broadcastMessage", broadcastMessage); try { await agent.call("broadcastMessage", [broadcastMessage]); addLog("in", "broadcast_sent"); toast("Broadcast sent", "info"); } catch (e) { addLog("error", "error", e instanceof Error ? e.message : String(e)); } }; const openNewTab = () => { window.open(window.location.href, "_blank"); }; return ( Agents can track every connected WebSocket client. Override{" "} onConnect {" "} and{" "} onClose {" "} to react when clients join or leave, use{" "} this.getConnections() {" "} to enumerate them, and{" "} this.broadcast() {" "} to send a message to everyone at once. Open this page in another tab and watch the count update. } statusIndicator={ } >
{/* Controls */}
{/* Connection Count */}
Connected Clients
{connectionCount}

Open multiple tabs to see the count update in real-time

{/* Broadcast */}
Broadcast Message

Send a message to all connected clients (including yourself)

) => setBroadcastMessage(e.target.value) } onKeyDown={(e: React.KeyboardEvent) => e.key === "Enter" && handleBroadcast() } className="flex-1" placeholder="Message to broadcast" />
{/* Received Messages */}
Received Broadcasts
{receivedMessages.length === 0 ? ( ) : (
{receivedMessages.map((msg, i) => (
{msg.message}
{new Date(msg.timestamp).toLocaleTimeString()}
))}
)}
{/* Tips */}
Try this:
  1. Open this page in another browser tab
  2. Watch the connection count update
  3. Send a broadcast message from one tab
  4. See it appear in all tabs
{/* Logs */}
); }