Builds AI chat interfaces and conversational UI with streaming responses, context management, and multi-modal support. Use when creating ChatGPT-style interfaces, AI assistants, code copilots, or conversational agents. Handles streaming text, token limits, regeneration, feedback loops, tool usage visualization, and AI-specific error patterns. Provides battle-tested components from leading AI products with accessibility and performance built in.
npx skills add https://github.com/ancoleman/ai-design-components --skill building-ai-chat
Define the emerging standards for AI/human conversational interfaces in the 2024-2025 AI integration boom. This skill leverages meta-knowledge from building WITH Claude to establish definitive patterns for streaming UX, context management, and multi-modal interactions. As the industry lacks established patterns, this provides the reference implementation others will follow.
Activate this skill when:
Minimal AI chat interface in under 50 lines:
import { useChat } from 'ai/react';
export function MinimalAIChat() {
const { messages, input, handleInputChange, handleSubmit, isLoading, stop } = useChat();
return (
<div className="chat-container">
<div className="messages">
{messages.map(m => (
<div key={m.id} className={`message ${m.role}`}>
<div className="content">{m.content}</div>
</div>
))}
{isLoading && <div className="thinking">AI is thinking...</div>}
</div>
<form onSubmit={handleSubmit} className="input-form">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask anything..."
disabled={isLoading}
/>
{isLoading ? (
<button type="button" onClick={stop}>Stop</button>
) : (
<button type="submit">Send</button>
)}
</form>
</div>
);
}
For complete implementation with streaming markdown, see examples/basic-chat.tsx.
Build user, AI, and system message bubbles with streaming support:
// User message
<div className="message user">
<div className="content">{message.content}</div>
<time className="timestamp">{formatTime(message.timestamp)}</time>
</div>
// AI message with streaming
<div className="message ai">
<Streamdown className="content">{message.content}</Streamdown>
{message.isStreaming && <span className="cursor">▊</span>}
</div>
// System message
<div className="message system">
<Icon type="info" />
<span>{message.content}</span>
</div>
For markdown rendering, code blocks, and formatting details, see references/message-components.md.
Create rich input experiences with attachments and voice:
<div className="input-container">
<button onClick={attachFile} aria-label="Attach file">
<PaperclipIcon />
</button>
<textarea
value={input}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder="Type a message..."
rows={1}
style={{ height: textareaHeight }}
/>
<button onClick={toggleVoice} aria-label="Voice input">
<MicIcon />
</button>
<button type="submit" disabled={!input.trim() || isLoading}>
<SendIcon />
</button>
</div>
Essential controls for AI responses:
<div className="response-controls">
{isStreaming && (
<button onClick={stop} className="stop-btn">
Stop generating
</button>
)}
{!isStreaming && (
<>
<button onClick={regenerate} aria-label="Regenerate response">
<RefreshIcon /> Regenerate
</button>
<button onClick={continueGeneration} aria-label="Continue">
Continue
</button>
<button onClick={editMessage} aria-label="Edit message">
<EditIcon /> Edit
</button>
</>
)}
</div>
Collect user feedback to improve AI responses:
<div className="feedback-controls">
<button
onClick={() => sendFeedback('positive')}
aria-label="Good response"
className={feedback === 'positive' ? 'selected' : ''}
>
<ThumbsUpIcon />
</button>
<button
onClick={() => sendFeedback('negative')}
aria-label="Bad response"
className={feedback === 'negative' ? 'selected' : ''}
>
<ThumbsDownIcon />
</button>
<button onClick={copyToClipboard} aria-label="Copy">
<CopyIcon />
</button>
<button onClick={share} aria-label="Share">
<ShareIcon />
</button>
</div>
Progressive rendering of AI responses requires special handling:
// Use Streamdown for AI streaming (handles incomplete markdown)
import { Streamdown } from '@vercel/streamdown';
// Auto-scroll management
useEffect(() => {
if (shouldAutoScroll()) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [messages]);
// Smart auto-scroll heuristic
function shouldAutoScroll() {
const threshold = 100; // px from bottom
const isNearBottom =
container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
const userNotReading = !hasUserScrolledUp && !isTextSelected;
return isNearBottom && userNotReading;
}
For complete streaming patterns, auto-scroll behavior, and stop generation, see references/streaming-ux.md.
Communicate token limits clearly to users:
// User-friendly token display
function TokenIndicator({ used, total }) {
const percentage = (used / total) * 100;
const remaining = total - used;
return (
<div className="token-indicator">
<div className="progress-bar">
<div className="progress-fill" style={{ width: `${percentage}%` }} />
</div>
<span className="token-text">
{percentage > 80
? `⚠️ About ${Math.floor(remaining / 250)} messages left`
: `${Math.floor(remaining / 250)} pages of conversation remaining`}
</span>
</div>
);
}
For summarization strategies, conversation branching, and organization, see references/context-management.md.
Handle images, files, and voice inputs:
// Image upload with preview
function ImageUpload({ onUpload }) {
return (
<div
className="upload-zone"
onDrop={handleDrop}
onDragOver={preventDefault}
>
<input
type="file"
accept="image/*"
onChange={handleFileSelect}
multiple
hidden
ref={fileInputRef}
/>
{previews.map(preview => (
<img key={preview.id} src={preview.url} alt="Upload preview" />
))}
</div>
);
}
For complete multi-modal patterns including voice and screen sharing, see references/multi-modal.md.
Handle AI-specific errors gracefully:
// Refusal handling
if (response.type === 'refusal') {
return (
<div className="error refusal">
<Icon type="info" />
<p>I cannot help with that request.</p>
<details>
<summary>Why?</summary>
<p>{response.reason}</p>
</details>
<p>Try asking: {response.suggestion}</p>
</div>
);
}
// Rate limit communication
if (error.code === 'RATE_LIMIT') {
return (
<div className="error rate-limit">
<p>Please wait {error.retryAfter} seconds</p>
<CountdownTimer seconds={error.retryAfter} onComplete={retry} />
</div>
);
}
For comprehensive error patterns, see references/error-handling.md.
Show when AI is using tools or functions:
function ToolUsage({ tool }) {
return (
<div className="tool-usage">
<div className="tool-header">
<Icon type={tool.type} />
<span>{tool.name}</span>
{tool.status === 'running' && <Spinner />}
</div>
{tool.status === 'complete' && (
<details>
<summary>View details</summary>
<pre>{JSON.stringify(tool.result, null, 2)}</pre>
</details>
)}
</div>
);
}
For function calling, code execution, and web search patterns, see references/tool-usage.md.
Primary libraries (validated November 2025):
# Core AI chat functionality
npm install ai @ai-sdk/react @ai-sdk/openai
# Streaming markdown rendering
npm install @vercel/streamdown
# Syntax highlighting
npm install react-syntax-highlighter
# Security for LLM outputs
npm install dompurify
Critical for smooth streaming:
// Memoize message rendering
const MemoizedMessage = memo(Message, (prev, next) =>
prev.content === next.content && prev.isStreaming === next.isStreaming
);
// Debounce streaming updates
const debouncedUpdate = useMemo(
() => debounce(updateMessage, 50),
[]
);
// Virtual scrolling for long conversations
import { VariableSizeList } from 'react-window';
For detailed performance patterns, see references/streaming-ux.md.
Always sanitize AI outputs:
import DOMPurify from 'dompurify';
function SafeAIContent({ content }) {
const sanitized = DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'code', 'pre', 'blockquote', 'ul', 'ol', 'li'],
ALLOWED_ATTR: ['class']
});
return <Streamdown>{sanitized}</Streamdown>;
}
Ensure AI chat is usable by everyone:
// ARIA live regions for screen readers
<div role="log" aria-live="polite" aria-relevant="additions">
{messages.map(msg => (
<article key={msg.id} role="article" aria-label={`${msg.role} message`}>
{msg.content}
</article>
))}
</div>
// Loading announcements
<div role="status" aria-live="polite" className="sr-only">
{isLoading ? 'AI is responding' : ''}
</div>
For complete accessibility patterns, see references/accessibility.md.
scripts/parse_stream.js to parse incomplete markdown during streamingscripts/calculate_tokens.py to estimate token usage and context limitsscripts/format_messages.js to format message history for exportreferences/streaming-patterns.md - Complete streaming UX patternsreferences/context-management.md - Token limits and conversation strategiesreferences/multimodal-input.md - Image, file, and voice handlingreferences/feedback-loops.md - User feedback and RLHF patternsreferences/error-handling.md - AI-specific error scenariosreferences/tool-usage.md - Visualizing function calls and tool usereferences/accessibility-chat.md - Screen reader and keyboard supportreferences/library-guide.md - Detailed library documentationreferences/performance-optimization.md - Streaming performance patternsexamples/basic-chat.tsx - Minimal ChatGPT-style interfaceexamples/streaming-chat.tsx - Advanced streaming with memoizationexamples/multimodal-chat.tsx - Images and file uploadsexamples/code-assistant.tsx - IDE-style code copilotexamples/tool-calling-chat.tsx - Function calling visualizationassets/system-prompts.json - Curated prompts for different use casesassets/message-templates.json - Pre-built message componentsassets/error-messages.json - User-friendly error messagesassets/themes.json - Light, dark, and high-contrast themesAll visual styling uses the design-tokens system:
/* Message bubbles use design tokens */
.message.user {
background: var(--message-user-bg, var(--color-primary));
color: var(--message-user-text, var(--color-white));
padding: var(--message-padding, var(--spacing-md));
border-radius: var(--message-border-radius, var(--radius-lg));
}
.message.ai {
background: var(--message-ai-bg, var(--color-gray-100));
color: var(--message-ai-text, var(--color-text-primary));
}
See skills/design-tokens/ for complete theming system.
This skill provides industry-first solutions for:
This is THE most critical skill because:
Master this skill to lead the AI interface revolution.
> Aggregates PayPal disputes, HubSpot feedback and tickets, and email sentiment (plus pasted or exported Google/Yelp reviews) into a themes report with verbatim evidence and a "do these three things this week" list. Use when the user asks how customers are feeling, for review analysis, what people are saying, or about disputes.
> dark mode, multi-provider sending, and spam score optimization. Use when setting up transactional email, building email design systems, or debugging deliverability.
Weekly read on Discord community health — joins, active channels, top contributors, themes, and unanswered questions
Create songs on Suno.com with custom lyrics and style. Use when user wants to (1) generate AI music on Suno with specific lyrics or theme, (2) create a song in a specific artist's style (e.g., G.E.M.邓紫棋), (3) download generated songs and send to Telegram. Triggers: create a song on Suno, generate a song, 用Suno创作歌曲, Suno创作, 下载Suno歌曲
> Focused Signals scout for PostHog projects running Replay Vision scanners — the standing LLM probes that watch session recordings and write `$recording_observed` events. Watches cliffs, exhausted quota — a silent watch gap), and that what the scanners see in aggregate gets surfaced (a monitor's `yes`-rate or a scorer's score stepping away from its own baseline, a classifier tag or a recurring summarizer theme concentrating across many `emits_signals` already emit one signal per session into this same inbox, so this scout never repeats them — it adds the cross-session shape the per-session probe can't see. Emits findings only when they clear the confidence bar; otherwise writes durable memory and closes out empty. Self-contained peer in the signals-scout-* fleet.
Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
Take ancoleman/building-ai-chat from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm.
Without those the skill loads but fails at the first command.