Chat SDK
Overview
The Chat SDK is fully customizable - override any component, apply custom styles, create themes, and make the chat interface match your brand perfectly.
Styling System
Default Styles
Import the default CSS:
import 'react-chat-agent/ui.css';CSS Variables
The SDK uses CSS variables for easy theming:
:root {
/* Colors */
--chat-background: #ffffff;
--chat-text: #000000;
--chat-message-user-bg: #0066ff;
--chat-message-agent-bg: #f5f5f5;
--chat-border: #e0e0e0;
/* Spacing */
--chat-padding: 1rem;
--chat-message-spacing: 0.5rem;
/* Fonts */
--chat-font-family: 'Inter', sans-serif;
--chat-font-size: 14px;
/* Radius */
--chat-border-radius: 12px;
}Dark Theme
Toggle between themes:
<ChatProvider
authConfig={authConfig}
isDark={true} // Enable dark theme
>
<ChatPanel isDark={true} />
</ChatProvider>Or use CSS:
[data-theme="dark"] {
--chat-background: #1a1a1a;
--chat-text: #ffffff;
--chat-message-user-bg: #0052cc;
--chat-message-agent-bg: #2d2d2d;
--chat-border: #404040;
}Custom CSS Classes
Every component has customizable classes:
ChatCore Classes
/* Container */
.react-chat-agent {
/* Your custom styles */
}
.react-chat-core {
background: #fafafa;
border-radius: 20px;
}
/* Messages container */
.react-chat-core-messages {
padding: 2rem;
}
/* Virtual container */
.react-chat-core-virtual-container {
/* Virtualization wrapper */
}
/* Load more button */
.react-chat-core-load-more {
margin: 1rem 0;
}
.react-chat-core-load-more-button {
color: #666;
transition: all 0.2s;
}
.react-chat-core-load-more-button:hover {
color: #000;
}
/* Loading spinner */
.react-chat-core-load-more-spinner {
animation: spin 1s linear infinite;
}Message Classes
/* Message items */
.react-chat-agent-message {
margin-bottom: 1rem;
}
.react-chat-agent-message-user {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 18px;
padding: 12px 16px;
}
.react-chat-agent-message-assistant {
background: #f5f5f5;
color: #000;
border-radius: 18px;
padding: 12px 16px;
}
.react-chat-agent-message-system {
background: #fff3cd;
border: 1px solid #ffc107;
}Input Classes
.react-chat-agent-input {
border: 2px solid #e0e0e0;
border-radius: 24px;
padding: 12px 20px;
font-size: 15px;
}
.react-chat-agent-input:focus {
border-color: #0066ff;
outline: none;
}
.react-chat-agent-send-button {
background: #0066ff;
color: white;
border-radius: 50%;
width: 40px;
height: 40px;
}Panel Classes
.react-chat-agent-panel {
height: 100vh;
display: flex;
flex-direction: column;
}
.react-chat-agent-header {
padding: 1rem;
border-bottom: 1px solid #e0e0e0;
}
.react-chat-agent-footer {
padding: 1rem;
border-top: 1px solid #e0e0e0;
}Custom Themes
Brand Theme
Create your brand's theme:
/* Acme Corp Theme */
.acme-chat {
--chat-primary: #ff6b35;
--chat-secondary: #004e89;
--chat-accent: #f7b801;
font-family: 'Poppins', sans-serif;
}
.acme-chat .react-chat-agent-message-user {
background: var(--chat-primary);
}
.acme-chat .react-chat-agent-send-button {
background: var(--chat-accent);
color: #000;
}
.acme-chat .react-chat-agent-input:focus {
border-color: var(--chat-primary);
}Apply the theme:
<div className="acme-chat">
<ChatProvider authConfig={authConfig}>
<ChatPanel />
</ChatProvider>
</div>Minimal Theme
.minimal-chat {
--chat-background: #fff;
--chat-border-radius: 4px;
--chat-padding: 0.75rem;
}
.minimal-chat .react-chat-agent-message {
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.minimal-chat .react-chat-agent-input {
border: none;
border-bottom: 2px solid #000;
border-radius: 0;
}Glassmorphism Theme
.glass-chat {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.glass-chat .react-chat-agent-message {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.glass-chat .react-chat-agent-input {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
}Custom Components
Custom Welcome Screen
function CustomWelcome() {
return (
<div className="custom-welcome">
<img src="/logo.svg" alt="Logo" />
<h2>Welcome to Support Chat</h2>
<p>How can we help you today?</p>
<div className="quick-actions">
<button>Track Order</button>
<button>Technical Support</button>
<button>Billing Question</button>
</div>
</div>
);
}
<ChatProvider
authConfig={authConfig}
initialWelcome={<CustomWelcome />}
>
<ChatPanel />
</ChatProvider>Custom Loading Component
function CustomLoading() {
return (
<div className="custom-loading">
<div className="spinner-container">
<div className="spinner" />
<p>Agent is thinking...</p>
</div>
</div>
);
}
<ChatProvider
authConfig={authConfig}
loadingComponent={CustomLoading}
>
<ChatPanel />
</ChatProvider>Custom Status Components
const customStatusComponents = {
tool_call: ({ message }) => (
<div className="tool-status">
{message.statusType === 'started' ? (
<div className="executing">
<Spinner />
<span>Executing {message.tool_name}...</span>
</div>
) : (
<div className="completed">
<CheckIcon />
<span>{message.tool_name} completed</span>
</div>
)}
</div>
),
edit_file: ({ message }) => (
<div className="file-status">
<FileIcon />
<span>{message.file_path}</span>
<Badge>{message.status.state}</Badge>
</div>
)
};
<ChatProvider
authConfig={authConfig}
statusComponents={customStatusComponents}
>
<ChatPanel />
</ChatProvider>Custom Markdown Components
const markdownComponents = {
code: ({ inline, className, children }) => {
if (inline) {
return <code className="inline-code">{children}</code>;
}
return (
<CodeBlock
language={className?.replace('language-', '')}
code={children}
/>
);
},
a: ({ href, children }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
img: ({ src, alt }) => (
<img src={src} alt={alt} loading="lazy" />
)
};
<ChatProvider
authConfig={authConfig}
markdownComponents={markdownComponents}
>
<ChatPanel />
</ChatProvider>Layout Customization
Custom Chat Layout
Build your own layout with ChatCore and ChatInput:
function CustomChatLayout() {
const coreRef = useRef();
return (
<div className="custom-layout">
{/* Custom Header */}
<header className="chat-header">
<Avatar src="/agent.png" />
<div>
<h3>Support Agent</h3>
<span className="status">Online</span>
</div>
<button onClick={() => coreRef.current?.abort()}>
Stop
</button>
</header>
{/* Messages */}
<ChatCore
ref={coreRef}
className="flex-1"
containerStyle={{
padding: '2rem',
background: 'linear-gradient(to bottom, #f0f0f0, #ffffff)'
}}
useGradientMask={true}
gradientMaskStyle="linear-gradient(to bottom, transparent, black 20%)"
/>
{/* Custom Footer */}
<footer className="chat-footer">
<div className="quick-replies">
<button>Order Status</button>
<button>Returns</button>
<button>Support</button>
</div>
<ChatInput placeholder="Type your message..." />
</footer>
</div>
);
}Split View
function SplitViewChat() {
return (
<div className="flex h-screen">
{/* Sidebar */}
<aside className="w-64 bg-gray-100 p-4">
<h3>Conversations</h3>
<ConversationList />
</aside>
{/* Chat */}
<main className="flex-1">
<ChatProvider authConfig={authConfig}>
<ChatPanel />
</ChatProvider>
</main>
{/* Details */}
<aside className="w-80 bg-gray-50 p-4">
<h3>Details</h3>
<AgentInfo />
<QuickActions />
</aside>
</div>
);
}Modal Chat
function ModalChat({ isOpen, onClose }) {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<div className="modal-chat h-96 w-96">
<ChatProvider authConfig={authConfig}>
<div className="flex flex-col h-full">
<div className="flex items-center justify-between p-4 border-b">
<h3>Chat Support</h3>
<button onClick={onClose}>×</button>
</div>
<ChatCore className="flex-1" />
<ChatInput />
</div>
</ChatProvider>
</div>
</Modal>
);
}Animation Customization
Typing Animation
Control how responses appear:
<ChatProvider
authConfig={authConfig}
typingAnimation={{
mode: 'char', // 'char' | 'word' | 'batch' | 'instant'
speed: 50, // Characters per second
batchSize: 10 // For 'batch' mode
}}
>
<ChatPanel />
</ChatProvider>Custom Animations
/* Message entrance */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.react-chat-agent-message {
animation: slideIn 0.3s ease-out;
}
/* Typing indicator */
@keyframes pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
.loading-dot {
animation: pulse 1.4s infinite;
}Responsive Design
Make chat work on all devices:
/* Mobile */
@media (max-width: 768px) {
.react-chat-agent-panel {
height: 100vh;
position: fixed;
inset: 0;
}
.react-chat-agent-message {
max-width: 85%;
}
.react-chat-agent-input {
font-size: 16px; /* Prevent zoom on iOS */
}
}
/* Tablet */
@media (min-width: 769px) and (max-width: 1024px) {
.react-chat-agent-panel {
max-width: 600px;
margin: 0 auto;
}
}
/* Desktop */
@media (min-width: 1025px) {
.react-chat-agent-panel {
max-width: 800px;
}
}Accessibility
ARIA Labels
<ChatInput
placeholder="Type your message..."
aria-label="Chat message input"
aria-describedby="chat-help-text"
/>
<button
onClick={sendMessage}
aria-label="Send message"
>
<SendIcon />
</button>Keyboard Navigation
/* Focus styles */
.react-chat-agent-input:focus {
outline: 2px solid #0066ff;
outline-offset: 2px;
}
.react-chat-agent-send-button:focus {
outline: 2px solid #0066ff;
outline-offset: 2px;
}
/* High contrast mode */
@media (prefers-contrast: high) {
.react-chat-agent-message {
border: 2px solid currentColor;
}
}Screen Reader Support
<div role="log" aria-live="polite" aria-atomic="false">
<ChatCore />
</div>Performance Optimization
Lazy Loading
import { lazy, Suspense } from 'react';
const ChatPanel = lazy(() => import('react-chat-agent').then(m => ({
default: m.ChatPanel
})));
function App() {
return (
<Suspense fallback={<Loading />}>
<ChatProvider authConfig={authConfig}>
<ChatPanel />
</ChatProvider>
</Suspense>
);
}Virtual Scrolling
ChatCore uses TanStack Virtual for efficient rendering:
<ChatCore
className="h-full"
// Automatically virtualizes messages
// Only renders visible messages
// Handles thousands of messages efficiently
/>CSS-in-JS
Use styled-components or emotion:
import styled from 'styled-components';
const StyledChatPanel = styled.div`
.react-chat-agent-message-user {
background: linear-gradient(135deg, ${props => props.theme.primary}, ${props => props.theme.secondary});
}
.react-chat-agent-input {
border-color: ${props => props.theme.border};
&:focus {
border-color: ${props => props.theme.primary};
}
}
`;
function ThemedChat() {
return (
<ThemeProvider theme={myTheme}>
<StyledChatPanel>
<ChatProvider authConfig={authConfig}>
<ChatPanel />
</ChatProvider>
</StyledChatPanel>
</ThemeProvider>
);
}Tailwind CSS
Style with Tailwind utility classes:
<div className="bg-white rounded-2xl shadow-lg">
<ChatCore
className="h-[600px] px-6 py-4"
containerStyle={{
scrollbarWidth: 'thin',
scrollbarColor: '#cbd5e0 #f7fafc'
}}
/>
<div className="border-t border-gray-200 p-4">
<ChatInput className="rounded-full border-2 border-gray-300 focus:border-blue-500" />
</div>
</div>Best Practices
CSS Organization
styles/
chat/
theme.css # Theme variables
components.css # Component styles
animations.css # Animations
responsive.css # Media queries
custom.css # Your customizationsPerformance
/* ✅ Use transform for animations */
.message {
transform: translateY(0);
transition: transform 0.3s;
}
/* ❌ Avoid animating layout properties */
.message {
margin-top: 0;
transition: margin-top 0.3s;
}Maintainability
/* ✅ Use CSS variables for consistency */
:root {
--chat-spacing: 1rem;
--chat-primary: #0066ff;
}
.message {
padding: var(--chat-spacing);
background: var(--chat-primary);
}
/* ❌ Hardcoded values everywhere */
.message {
padding: 16px;
background: #0066ff;
}Next Steps
- Getting Started - Set up the SDK
- Components - Explore components
- Examples - See live examples