UNPKG

@copilotkit/react-core

Version:

<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />

161 lines (139 loc) 4.65 kB
# Agent Switcher Recipes Three copy-paste patterns for multi-agent UIs. All subscribe to `copilotkit.subscribe({ onAgentsChanged })` for live agent discovery — there is no `useAgents()` hook. ## Recipe 1 — Dropdown switcher ```tsx "use client"; import { CopilotChat, useCopilotKit } from "@copilotkit/react-core/v2"; import { useEffect, useState } from "react"; export function DropdownAgentSwitcher() { const { copilotkit } = useCopilotKit(); const [agentIds, setAgentIds] = useState<string[]>(() => Object.keys(copilotkit.agents ?? {}), ); const [activeAgent, setActiveAgent] = useState<string>( () => Object.keys(copilotkit.agents ?? {})[0] ?? "default", ); useEffect(() => { const sub = copilotkit.subscribe({ onAgentsChanged: ({ agents }) => { setAgentIds(Object.keys(agents ?? {})); }, }); return () => sub.unsubscribe(); }, [copilotkit]); return ( <div className="flex flex-col gap-3"> <select value={activeAgent} onChange={(e) => setActiveAgent(e.target.value)} > {agentIds.map((id) => ( <option key={id} value={id}> {id} </option> ))} </select> <CopilotChat key={activeAgent} agentId={activeAgent} /> </div> ); } ``` ## Recipe 2 — Tabs switcher ```tsx "use client"; import { CopilotChat, useCopilotKit } from "@copilotkit/react-core/v2"; import { useEffect, useRef, useState } from "react"; export function TabsAgentSwitcher() { const { copilotkit } = useCopilotKit(); const [agentIds, setAgentIds] = useState<string[]>(() => Object.keys(copilotkit.agents ?? {}), ); const [activeAgent, setActiveAgent] = useState<string>( () => agentIds[0] ?? "default", ); // Hold activeAgent in a ref so the subscribe effect only re-binds when // `copilotkit` changes. Depending on `activeAgent` would tear down and // re-establish the subscription on every tab click. const activeAgentRef = useRef(activeAgent); useEffect(() => { activeAgentRef.current = activeAgent; }, [activeAgent]); useEffect(() => { const sub = copilotkit.subscribe({ onAgentsChanged: ({ agents }) => { const ids = Object.keys(agents ?? {}); setAgentIds(ids); if (!ids.includes(activeAgentRef.current) && ids.length > 0) { setActiveAgent(ids[0]); } }, }); return () => sub.unsubscribe(); }, [copilotkit]); return ( <div> <div role="tablist" className="flex gap-2 border-b"> {agentIds.map((id) => ( <button key={id} role="tab" aria-selected={id === activeAgent} onClick={() => setActiveAgent(id)} > {id} </button> ))} </div> <CopilotChat key={activeAgent} agentId={activeAgent} /> </div> ); } ``` ## Recipe 3 — Keyboard shortcut switcher Cycles through agents with `Cmd/Ctrl + Shift + A`. ```tsx "use client"; import { CopilotChat, useCopilotKit } from "@copilotkit/react-core/v2"; import { useEffect, useState } from "react"; export function KeyboardAgentSwitcher() { const { copilotkit } = useCopilotKit(); const [agentIds, setAgentIds] = useState<string[]>(() => Object.keys(copilotkit.agents ?? {}), ); const [activeAgent, setActiveAgent] = useState<string>( () => agentIds[0] ?? "default", ); useEffect(() => { const sub = copilotkit.subscribe({ onAgentsChanged: ({ agents }) => setAgentIds(Object.keys(agents ?? {})), }); return () => sub.unsubscribe(); }, [copilotkit]); useEffect(() => { function onKey(e: KeyboardEvent) { const isCombo = (e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "A"; if (!isCombo || agentIds.length === 0) return; e.preventDefault(); const idx = agentIds.indexOf(activeAgent); const next = agentIds[(idx + 1) % agentIds.length]; setActiveAgent(next); } window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [agentIds, activeAgent]); return ( <div> <div className="text-xs opacity-60"> Active: {activeAgent} — press ⌘/Ctrl+Shift+A to cycle </div> <CopilotChat key={activeAgent} agentId={activeAgent} /> </div> ); } ``` ## Key rules across all three recipes - Use `copilotkit.subscribe({ onAgentsChanged })` — there is no `useAgents()` hook. - Always `key={activeAgent}` on `<CopilotChat>` so thread state doesn't leak when swapping agents in the same slot. - Clean up the subscription with `sub.unsubscribe()` in the effect cleanup.