Build professional AI chat interfaces using the useXChat Hook.

Install

mkdir -p .claude/skills/use-x-chat && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12541" && unzip -o skill.zip -d .claude/skills/use-x-chat && rm skill.zip

Installs to .claude/skills/use-x-chat

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

专注讲解如何使用 useXChat Hook,包括自定义 Provider 的集成、消息管理、错误处理、多会话管理等
58 charsno explicit “when” trigger
Advanced

Key capabilities

  • Integrate custom Provider
  • Manage messages
  • Handle errors
  • Manage multiple sessions
  • Map `MessageInfo` to `Bubble.List` format
  • Render complex `ChatMessage` objects

How it works

The skill integrates a custom chat provider, manages messages, and handles errors using `useXChat` Hook. It maps `MessageInfo` to the `Bubble.List` format and renders complex `ChatMessage` objects.

Inputs & outputs

You give it
chat messages and user requests
You get back
rendered chat UI with message management and error handling

When to use use-x-chat

  • Implementing custom chat providers
  • Handling chat error states
  • Building multi-session chat interfaces

About this skill

🎯 技能定位

核心定位:使用 useXChat Hook 构建专业级 AI 对话应用 前置要求:已具备自定义 Chat Provider(参考 x-chat-provider 技能

目录导航

🚀 快速开始

1. 依赖管理

  • @ant-design/x-sdk: 2.2.2+
  • @ant-design/x: 最新版(UI 组件)
npm install @ant-design/x-sdk@latest @ant-design/x@latest

2. 三步集成

步骤1:准备 Provider

由 x-chat-provider 技能负责。注意 XRequest 必须传 manual: true

import { MyChatProvider } from './MyChatProvider';
import { XRequest } from '@ant-design/x-sdk';

// ⚠️ manual: true 是必须的
const provider = new MyChatProvider({
  request: XRequest('https://your-api.com/chat', { manual: true }),
});

步骤2:基础使用

import { useXChat } from '@ant-design/x-sdk';

const ChatComponent = () => {
  const { messages, onRequest, isRequesting } = useXChat({
    provider,
    requestPlaceholder: (_, { messages }) => ({
      content: '正在思考中...',
      role: 'assistant',
    }),
    requestFallback: (_, { error, messageInfo }) => {
      if (error.name === 'AbortError') {
        return { content: messageInfo?.message?.content || '已取消回复', role: 'assistant' };
      }
      return { content: '网络异常,请稍后重试', role: 'assistant' };
    },
  });

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>
          {msg.message.role}: {msg.message.content}
        </div>
      ))}
      <button onClick={() => onRequest({ query: '你好' })}>发送</button>
    </div>
  );
};

步骤3:UI 集成

⚠️ messagesMessageInfo<ChatMessage>[],不能直接传给 Bubble.List。需要映射为 { key, role, content, loading } 格式。 Bubble.List 使用 role 属性(不是 roles)配置角色样式。

import { Bubble, Sender } from '@ant-design/x';

const ChatUI = () => {
  const { messages, onRequest, isRequesting, abort } = useXChat({ provider });

  return (
    <div style={{ height: 600 }}>
      <Bubble.List
        // ✅ 正确:使用 role(不是 roles)
        role={{
          user: { placement: 'end' },
          assistant: { placement: 'start' },
        }}
        items={messages.map(({ id, message, status }) => ({
          key: id,
          role: message.role, // 用于匹配 role 配置
          content: message.content, // 消息内容
          loading: status === 'loading', // 加载动画
        }))}
      />
      <Sender
        loading={isRequesting}
        onSubmit={(content) => onRequest({ query: content })}
        onCancel={abort}
      />
    </div>
  );
};

当 ChatMessage 是对象类型时(非字符串)

ChatMessage 是复杂对象(如有 contentattachments 等字段),需用 contentRender 渲染:

<Bubble.List
  role={{
    assistant: {
      placement: 'start',
      // contentRender 接收 content 参数,就是 message 字段本身
      contentRender(content: MyMessage) {
        return (
          <div>
            <div>{content.content}</div>
            {content.attachments?.map((a) => (
              <FileCard key={a.url} name={a.name} />
            ))}
          </div>
        );
      },
    },
    user: {
      placement: 'end',
      contentRender(content: MyMessage) {
        return content.content;
      },
    },
  }}
  items={messages.map(({ id, message, status }) => ({
    key: id,
    role: message.role,
    content: message, // ⚠️ 传整个 message 对象,contentRender 会处理渲染
    loading: status === 'loading',
  }))}
/>

🧩 核心概念

数据模型

⚠️ 重要messages 类型是 MessageInfo<ChatMessage>[],消息内容在 msg.message

interface MessageInfo<ChatMessage> {
  id: number | string; // 消息唯一标识
  message: ChatMessage; // 实际消息内容(你的 ChatMessage 类型)
  status: MessageStatus; // 消息状态
  extraInfo?: AnyObject; // 扩展信息(注意:是 extraInfo,不是 extra)
}

type MessageStatus = 'local' | 'loading' | 'updating' | 'success' | 'error' | 'abort';
// local: 用户发送的本地消息
// loading: AI 回复占位中(requestPlaceholder 对应此状态)
// updating: AI 正在流式输出
// success: AI 回复完成
// error: 请求失败
// abort: 用户主动取消

useXChat 配置项

配置项类型说明
providerAbstractChatProvider<ChatMessage, Input, Output>必填,Provider 实例
conversationKeystring会话唯一标识,多会话必填
defaultMessagesDefaultMessageInfo[] | () => ... | async () => ...默认展示消息,支持异步加载
requestPlaceholderChatMessage | (requestParams, { messages }) => ChatMessage请求中的占位消息
requestFallbackChatMessage | (requestParams, { error, errorInfo, messages, messageInfo }) => ChatMessage | Promise<ChatMessage>请求失败/中止时的兜底消息
parser(message: ChatMessage) => BubbleMessage | BubbleMessage[]将 ChatMessage 转为组件消费格式,支持一转多

requestFallbackmessageInfo 类型为 MessageInfo<ChatMessage>,是请求失败时正在更新的那条消息。 requestFallback 同时处理网络错误(error)和主动取消(error.name === 'AbortError')。

useXChat 返回值

返回值类型说明
messagesMessageInfo<ChatMessage>[]消息列表,需映射后传给 Bubble.List
parsedMessagesMessageInfo<ParsedMessage>[]经过 parser 转译后的消息列表(有 parser 时用这个)
onRequest(params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void添加消息并触发请求
isRequestingboolean是否正在请求
abort() => void中止当前请求
setMessages(messages: Partial<MessageInfo<ChatMessage>>[]) => void直接修改消息列表,不触发请求
setMessage(id: string | number, info: Partial<MessageInfo<ChatMessage>>) => void修改单条消息,不触发请求
removeMessage(id: string | number) => boolean删除某条消息,返回是否删除成功
onReload(id: string | number, params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void重新生成某条 AI 回复
queueRequest(conversationKey: string | symbol, params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void队列化请求,等待会话初始化后发送
isDefaultMessagesRequestingboolean默认消息是否在异步加载中

🔧 核心功能详解

核心功能参考 CORE.md

🗂️ 多会话管理

useXConversations Hook

useXConversations@ant-design/x-sdk 提供的会话列表管理 Hook,与 useXChat 配合实现多会话:

import { useXConversations } from '@ant-design/x-sdk';
import type { ConversationData } from '@ant-design/x-sdk';

const {
  conversations, // ConversationData[]:会话列表
  activeConversationKey, // string:当前激活会话的 key
  setActiveConversationKey, // (key: string) => void:切换会话
  addConversation, // ( ConversationData, placement?) => boolean
  removeConversation, // (key: string) => boolean
  setConversation, // (key: string, data: ConversationData) => boolean
  getConversation, // (key: string) => ConversationData | undefined
  setConversations, // (list: ConversationData[]) => void
  getMessages, // (key: string) => MessageInfo[] | undefined(跨组件读取消息)
} = useXConversations({
  defaultConversations: [
    { key: 'conv-1', label: '会话 1' },
    { key: 'conv-2', label: '会话 2' },
  ],
  defaultActiveConversationKey: 'conv-1',
});

多会话完整模式

import { useXChat, useXConversations } from '@ant-design/x-sdk';
import { OpenAIChatProvider, XRequest } from '@ant-design/x-sdk';
import { Bubble, Conversations, Sender } from '@ant-design/x';
import React, { useEffect, useRef } from 'react';

// ⚠️ 每个会话必须有独立的 Provider 实例,否则状态会混用
const providerCache = new Map<string, OpenAIChatProvider>();

function getProvider(key: string): OpenAIChatProvider {
  if (!providerCache.has(key)) {
    providerCache.set(
      key,
      new OpenAIChatProvider({
        request: XRequest(BASE_URL, { manual: true, params: { model: 'gpt-4o', stream: true } }),
      }),
    );
  }
  return providerCache.get(key)!;
}

const App = () => {
  const senderRef = useRef<any>(null);

  const { conversations, activeConversationKey, setActiveConversationKey, addConversation } =
    useXConversations({
      defaultConversations: [{ key: 'conv-1', label: '新对话' }],
      defaultActiveConversationKey: 'conv-1',
    });

  const { messages, onRequest, isRequesting, abort, queueRequest } = useXChat({
    provider: getProvider(activeConversationKey),
    conversationKey: activeConversationKey,
    // 异步加载默认消息
    defaultMessages: async ({ conversationKey }) => {
      // 根据 conversationKey 从服务器加载历史消息
      return [];
    },
    requestFallback: (_, { error, messageInfo }) => {
      if (error.name === 'AbortError') {
        return { content: messageInfo?.message?.content || '已取消', role: 'assistant' };
      }
      return { content: '请求失败', role: 'assistant' };
    },
  });

  // 切换会话时清空输入框
  useEffect(() => {
    senderRef.current?.clear?.();
  }, [activeConversationKey]);

  const handleNewConversation = () => {
    const newKey = `conv-${Date.now()}`;
    addConversation({ key: newKey, label: `新对话 ${conversations.length + 1}` });
    setActiveConversationKey(newKey);
  };

  return (
    <div style={{ display: 'flex', height: '100vh' }}>
      <Conversations
        items={conversations}
        activeKey={activeConversationKey}
        onActiveChange={setActiveConversationKey}
        creation={{ onClick: handleNewConversation }}
      />
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
        <Bubble.List
          role={{ assistant: { placement: 'start' }, user: { placement: 'end' } }}
          items={messages.map(({ id, message, status }) => ({
            key: id,
            role: message.role,
            content: message.content,
            loading: status === 'loading',
          }))}
        />
        <Sender
          ref={senderRef}
          loading={isRequesting}
          onCancel={abort}
          onSubmit={(val) => {
            onRequest({ messages: [{ role: 'user', content: val }] });
          }}
        />
      </div>
    </div>
  );
};

queueRequest:会话切换后延迟发送

// 场景:用户切换到新会话,同时触发一条初始消息
// queueRequest 会等 defaultMessages 异步加载完成后,再发送请求

const handleNewConversationWithFirstMessage = () => {
  const newKey = `conv-${Date.now()}`;
  addConversation({ key: newKey, label: '新对话' });
  setActiveConversationKey(newKey);

  // 把消息加入队列,等 newKey 会话的 defaultMessages 加载完后自动发送
  queueRequest(newKey, {
    messages: [{ role: 'user

---

*Content truncated.*

When not to use it

  • When not building professional AI dialogue applications
  • When not using `useXChat` Hook
  • When a custom Chat Provider is not already available

Prerequisites

@ant-design/x-sdk@ant-design/x

Limitations

  • Requires a custom Chat Provider to be already available
  • Requires `XRequest` in the Provider to be configured with `manual: true`
  • Requires `Bubble.List` to use the `role` property for styling

How it compares

This workflow provides specific guidance on integrating `useXChat` with custom providers and UI components, including handling complex message types and multi-session management, which is more detailed than a basic chat implementation.

Compared to similar skills

use-x-chat side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
use-x-chat (this skill)03moReviewAdvanced
convex-realtime16moNo flagsIntermediate
wagmi-development16moReviewAdvanced
query-layer12moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry