Xzero UI
Pro library

Agent handoffPro

A support conversation where an AI assistant hands off to a person. When it transfers, a card shows the summary it passes to the team and the typical wait. The person then joins and takes over, and a thin timeline on the left marks each step.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/agent-handoff
Live · keyboard ready

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/agent-handoff

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AgentHandoff } from "@/components/xzero/agent-handoff/agent-handoff";

// Demo: plays a simulated support chat
<AgentHandoff />

// Wired to your support backend
<AgentHandoff
  stage={conversation.stage}
  title="Order NW-48213"
  subtitle="Northwind Home support"
  messages={conversation.aiMessages}
  agentMessages={conversation.agentMessages}
  typing={conversation.typing}
  summary={{
    issue: "Desk arrived with a cracked leg. Customer wants a full refund.",
    fields: [{ label: "Order", value: "NW-48213" }, { label: "Asks for", value: "Full refund" }],
  }}
  agent={{ name: "Jordan Reyes", role: "Support specialist", avatar: "/jordan.jpg", joinedAt: "14:05" }}
  provider="intercom"
  onSend={text => sendMessage(text)}
  onRequestHuman={() => escalate()}
  onCancelTransfer={() => cancelEscalation()}
  onRetry={() => escalate()}
/>

API reference

AgentHandoff

A support conversation where an AI assistant hands off to a person, with a summary card and a step timeline. Leave out stage to play the built in simulated chat.

PropTypeDefaultDescription
stage"assistant" | "transferring" | "connected" | "resolved" | "unavailable"–Where the conversation is. Leave it out to play the simulated demo.
messagesHandoffMessage[][]The conversation with the assistant, before the handoff. Each message is { id, from: "customer" | "assistant" | "agent", text, time?, streaming? }.
agentMessagesHandoffMessage[][]Everything after the handoff card: notes the customer adds while waiting and the conversation with the person. The join step sits before the person's first message.
typing"assistant" | "agent" | nullnullWho is typing. The assistant shows a short shimmering Thinking line, a person shows typing dots.
summary{ issue: string; fields?: { label: string; value: string }[] }–What the assistant passes to the person. Shown in the handoff card from transferring on, collapsible.
assistant{ name?: string; model?: string; mark?: ReactNode }{ name: "Assistant", model: "Claude Sonnet 5.5" }The AI assistant on the first step. The mark defaults to the Claude mark; pass a 14px mark for another model.
agent{ name: string; role?: string; avatar?: string; joinedAt?: string }–The person who takes over. Shown on the join step, in the card and in the header from connected on.
provider"intercom" | "zendesk" | { name: string; mark: ReactNode }"intercom"The help desk the conversation moves to. Its mark sits on the timeline next to the handoff card.
replyEstimatestring"2 min"Typical wait shown while transferring.
titlestring"Support chat"Header title, such as the order or ticket number.
subtitlestring–Muted line under the title.
onSend(text: string) => void–Shows the composer. Called with the trimmed text; append the message to messages or agentMessages yourself.
onRequestHuman() => void–Shows a Talk to a person reply under the assistant's last answer while the stage is assistant.
onCancelTransfer() => void–Shows Cancel on the handoff card while transferring.
onRetry() => void–Shows Try again on the handoff card when the stage is unavailable.
placeholderstring–Composer placeholder. Defaults to one that follows the stage, such as Message Jordan.
heightnumber | string600Card height. The thread scrolls inside and stays pinned to the newest message.
noticestring–A one-off message for screen readers, such as Refund issued.
scenario"handoff" | "unavailable""handoff"Simulation only: unavailable stops when nobody picks the transfer up, so Try again shows.
classNamestring–Class on the root card.

useHandoffSimulation

The hook behind the demo: returns stage, messages, agentMessages, typing, summary, notice and the handlers, so you can drive the view yourself or prototype a flow.

PropTypeDefaultDescription
scenario (required)"handoff" | "unavailable"–Which story to play.
reduced (required)boolean–Reduced motion: replies appear whole and the loop plays once.

Keyboard interactions

KeysAction
TabMoves through the thread, Talk to a person, the card actions, the summary toggle and the composer.
EnterSends the composer message, or activates the focused button.
SpaceToggles the summary when the summary button has focus.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Also in ai

Guidance for AI tools

Agent handoff: A support conversation where an AI assistant hands off to a person. When it transfers, a card shows the summary it passes to the team and the typical wait. The person then joins and takes over, and a thin timeline on the left marks each step. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components