
use-x-chat
Focus on explaining how to use the useXChat Hook, including custom Provider integration, message management, error handling, multi-conversation management, and more
- Installs
- 92
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/ant-design/x --skill use-x-chat- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads use-x-chat automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
🎯 Skill Positioning
Core Positioning: Use the
useXChatHook to build professional AI conversation applications. Prerequisite: Already have a custom Chat Provider (refer to x-chat-provider skill)
Table of Contents
- 🚀 Quick Start
- 🧩 Core Concepts
- 🔧 Core Function Details
- 🗂️ Multi-conversation Management
- 📋 Prerequisites and Dependencies
- 🚨 Development Rules
- 🔗 Reference Resources
🚀 Quick Start
1. Dependency Management
- @ant-design/x-sdk: 2.2.2+
- @ant-design/x: latest version (UI components)
npm install @ant-design/x-sdk@latest @ant-design/x@latest
2. Three-step Integration
Step 1: Prepare Provider
Handled by the x-chat-provider skill. Note XRequest must pass manual: true:
import { MyChatProvider } from './MyChatProvider';
import { XRequest } from '@ant-design/x-sdk';
// ⚠️ manual: true is required
const provider = new MyChatProvider({
request: XRequest('https://your-api.com/chat', { manual: true }),
});
Step 2: Basic Usage
import { useXChat } from '@ant-design/x-sdk';
const ChatComponent = () => {
const { messages, onRequest, isRequesting } = useXChat({
provider,
requestPlaceholder: (_, { messages }) => ({
content: 'Thinking...',
role: 'assistant',
}),
requestFallback: (_, { error, messageInfo }) => {
if (error.name === 'AbortError') {
return { content: messageInfo?.message?.content || 'Reply cancelled', role: 'assistant' };
}
return { content: 'Network error, please try again later', role: 'assistant' };
},
});
return (
<div>
{messages.map((msg) => (
<div key={msg.id}>
{msg.message.role}: {msg.message.content}
</div>
))}
<button onClick={() => onRequest({ query: 'Hello' })}>Send</button>
</div>
);
};
Step 3: UI Integration
⚠️
messagesisMessageInfo<ChatMessage>[]and cannot be passed directly toBubble.List. It must be mapped to{ key, role, content, loading }format.Bubble.Listuses theroleprop (notroles) to configure role styles.
import { Bubble, Sender } from '@ant-design/x';
const ChatUI = () => {
const { messages, onRequest, isRequesting, abort } = useXChat({ provider });
return (
<div style={{ height: 600 }}>
<Bubble.List
// ✅ Correct: use role (not roles)
role={{
user: { placement: 'end' },
assistant: { placement: 'start' },
}}
items={messages.map(({ id, message, status }) => ({
key: id,
role: message.role, // matches role config key
content: message.content, // message content
loading: status === 'loading', // loading animation
}))}
/>
<Sender
loading={isRequesting}
onSubmit={(content) => onRequest({ query: content })}
onCancel={abort}
/>
</div>
);
};
When ChatMessage is an object type (not string)
When ChatMessage is a complex object (e.g., with content, attachments fields), use contentRender:
<Bubble.List
role={{
assistant: {
placement: 'start',
// contentRender receives content param, which is the message field itself
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, // ⚠️ Pass the entire message object; contentRender handles rendering
loading: status === 'loading',
}))}
/>
🧩 Core Concepts
Data Model
⚠️ Important:
messagestype isMessageInfo<ChatMessage>[]; message content is inmsg.message
interface MessageInfo<ChatMessage> {
id: number | string; // Message unique identifier
message: ChatMessage; // Actual message content (your ChatMessage type)
status: MessageStatus; // Message status
extraInfo?: AnyObject; // Extended info (note: extraInfo, not extra)
}
type MessageStatus = 'local' | 'loading' | 'updating' | 'success' | 'error' | 'abort';
// local: locally sent user message
// loading: AI reply placeholder (corresponds to requestPlaceholder)
// updating: AI streaming output in progress
// success: AI reply complete
// error: request failed
// abort: user actively cancelled
useXChat Configuration Options
| Option | Type | Description |
|---|---|---|
provider | AbstractChatProvider<ChatMessage, Input, Output> | Required, Provider instance |
conversationKey | string | Conversation unique identifier, required for multi-conversation |
defaultMessages | DefaultMessageInfo[] | () => ... | async () => ... | Default display messages, supports async loading |
requestPlaceholder | ChatMessage | (requestParams, { messages }) => ChatMessage | Placeholder message during request |
requestFallback | ChatMessage | (requestParams, { error, errorInfo, messages, messageInfo }) => ChatMessage | Promise<ChatMessage> | Fallback message on request failure/abort |
parser | (message: ChatMessage) => BubbleMessage | BubbleMessage[] | Convert ChatMessage to component-consumable format, supports one-to-many |
requestFallback'smessageInfotype isMessageInfo<ChatMessage>, the message being updated when the request fails.requestFallbackhandles both network errors (error) and user abort (error.name === 'AbortError').
useXChat Return Values
| Return Value | Type | Description |
|---|---|---|
messages | MessageInfo<ChatMessage>[] | Message list; must be mapped before passing to Bubble.List |
parsedMessages | MessageInfo<ParsedMessage>[] | Message list after parser transform (use this when parser is set) |
onRequest | (params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void | Add message and trigger request |
isRequesting | boolean | Whether request is in progress |
abort | () => void | Abort current request |
setMessages | (messages: Partial<MessageInfo<ChatMessage>>[]) => void | Directly modify message list, no request triggered |
setMessage | (id: string | number, info: Partial<MessageInfo<ChatMessage>>) => void | Modify single message, no request triggered |
removeMessage | (id: string | number) => boolean | Delete a message, returns whether deletion was successful |
onReload | (id: string | number, params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void | Regenerate an AI reply |
queueRequest | (conversationKey: string | symbol, params: Partial<Input>, opts?: { extraInfo: AnyObject }) => void | Queue request, sent after conversation initializes |
isDefaultMessagesRequesting | boolean | Whether default messages are async loading |
🔧 Core Function Details
Core functionality reference: CORE.md
🗂️ Multi-conversation Management
useXConversations Hook
useXConversations is a conversation list management Hook provided by @ant-design/x-sdk, used together with useXChat for multi-conversation:
import { useXConversations } from '@ant-design/x-sdk';
import type { ConversationData } from '@ant-design/x-sdk';
const {
conversations, // ConversationData[]: conversation list
activeConversationKey, // string: currently active conversation key
setActiveConversationKey, // (key: string) => void: switch conversation
addConversation, // (ConversationData, placement?) => boolean
removeConversation, // (key: string) => boolean
setConversation, // (key: string, ConversationData) => boolean
getConversation, // (key: string) => ConversationData | undefined
setConversations, // (list: ConversationData[]) => void
getMessages, // (key: string) => MessageInfo[] | undefined (read messages across components)
} = useXConversations({
defaultConversations: [
{ key: 'conv-1', label: 'Conversation 1' },
{ key: 'conv-2', label: 'Conversation 2' },
],
defaultActiveConversationKey: 'conv-1',
});
Multi-conversation Full Pattern
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';
// ⚠️ Each conversation must have its own Provider instance, otherwise state mixes
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: 'New Conversation' }],
defaultActiveConversationKey: 'conv-1',
});
const { messages, onRequest, isRequesting, abort, queueRequest } = useXChat({
provider: getProvider(activeConversationKey),
conversationKey: activeConversationKey,
// Async load default messages
defaultMessages: async ({ conversationKey }) => {
// Load history from server based on conversationKey
return [];
},
requestFallback: (_, { error, messageInfo }) => {
if (error.name === 'AbortError') {
return { content: messageInfo?.message?.content || 'Cancelled', role: 'assistant' };
}
return { content: 'Request failed', role: 'assistant' };
},
});
// Clear input on conversation switch
useEffect(() => {
senderRef.current?.clear?.();
}, [activeConversationKey]);
const handleNewConversation = () => {
const newKey = `conv-${Date.now()}`;
addConversation({ key: newKey, label: `New Conversation ${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: Delayed Send After Conversation Switch
// Scenario: user switches to a new conversation and triggers an initial message simultaneously
// queueRequest waits for defaultMessages async loading to complete, then sends the request
const handleNewConversationWithFirstMessage = () => {
const newKey = `conv-${Date.now()}`;
addConversation({ key: newKey, label: 'New Conversation' });
setActiveConversationKey(newKey);
// Queue the message; sent automatically after newKey conversation's defaultMessages finish loading
queueRequest(newKey, {
messages: [{ role: 'user', content: 'Hello! Please introduce yourself.' }],
});
};
📋 Prerequisites and Dependencies
| Usage Scenario | Required Skill/Provider | Order |
|---|---|---|
| Private API Adaptation | x-chat-provider → use-x-chat | Create Provider first |
| Standard API | Built-in Provider + use-x-chat | Direct use |
| Multi-conversation | Provider factory + useXConversations + useXChat | Use together |
🚨 Development Rules
Before using use-x-chat, confirm:
- Has Provider (custom or built-in Provider)
- Provider's XRequest is configured with
manual: true - Understands
MessageInfodata structure (message content is inmsg.message) -
Bubble.Listusesroleprop (notroles) - Multi-conversation scenario: each conversation has its own Provider instance
Test Case Rules
- If the user does not explicitly need test cases, do not add test files
Code Quality Rules
- After completion, must check types: Run
tsc --noEmitto ensure no type errors - Keep code clean: Remove all unused variables and imports
🔗 Reference Resources
📚 Core Reference Documentation
- API.md - Complete API reference documentation
- CORE.md - Core function details
- EXAMPLES.md - Practical example code
🌐 SDK Official Documentation
- useXChat Official Documentation
- XRequest Official Documentation
- Chat Provider Official Documentation
💻 Example Code
- with-x-chat.tsx - Multi-conversation full example
- openai-callback.tsx - callbacks + removeMessage example
- developer.tsx - System prompt with developer role
- custom-provider-width-ui.tsx - Custom Provider full example
FAQ
What does the use-x-chat skill do?
Focus on explaining how to use the useXChat Hook, including custom Provider integration, message management, error handling, multi-conversation management, and more
How do I install use-x-chat?
Run npx skills add https://github.com/ant-design/x --skill use-x-chat in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/use-x-chat/ (personal) or .claude/skills/use-x-chat/ (project).
Which AI agents can use use-x-chat?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is use-x-chat safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains use-x-chat?
It is published by ant-design in ant-design/x. It has 92 recorded installs.
More from ant-design/x
x-markdown
ant-design/x
Use when building or reviewing Markdown rendering with @ant-design/x-markdown, including streaming Markdown, custom component mapping, plugins, themes, and chat-oriented rich content.
x-components
ant-design/x
Use when building AI chat UIs with @ant-design/x components — covers Bubble, Sender, Conversations, Prompts, ThoughtChain, Actions, Welcome, Attachments, Sources, Suggestion, Think, FileCard, CodeHighlighter, Mermaid, Folder, XProvider, and Notification.
x-request
ant-design/x
Focus on explaining the practical configuration and usage of XRequest, providing accurate configuration instructions based on official documentation
x-chat-provider
ant-design/x
Focus on implementing custom Chat Provider, helping to adapt any streaming interface to Ant Design X standard format
x-card
ant-design/x
Use when building AI-driven UIs with @ant-design/x-card — covers XCard.Box, XCard.Card, A2UI v0.9 commands, data binding, catalogs, actions, and streaming patterns.
Popular other skills
hyperframes-cli
heygen-com/hyperframes
The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures.
triage
mattpocock/skills
Move issues and external PRs through a state machine of triage roles, categorise, verify, grill if needed, and write agent-ready briefs.
ask-matt
mattpocock/skills
Ask which skill or flow fits your situation. A router over the skills in this repo.
to-questionnaire
mattpocock/skills
Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
loop-me
mattpocock/skills
Grill me about specs for the workflows I want to build, within this workspace.