Examples & Best Practices - Session Initialization
Real-world examples and best practices for RKT Chat Client session initialization
Examples & Best Practices
This guide provides real-world examples and best practices for implementing session initialization in various scenarios.
Basic Examples
Simple Chat Widget
A minimal chat widget with session initialization:
import React, { useState } from 'react';
import { useChat } from '@rkt/rkt-chat-client';
const SimpleChatWidget: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const {
sessionId,
isSessionReady,
messages,
sendMessage,
isChatLoading,
} = useChat({
chatbot: {
id: 'support-chatbot',
prompt: 'You are a helpful customer support assistant.',
},
baseUrl: 'https://api.example.com/chat',
});
if (!isSessionReady) {
return (
<div className="fixed bottom-4 right-4 w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center">
<div className="animate-spin w-6 h-6 border-2 border-white border-t-transparent rounded-full"></div>
</div>
);
}
return (
<div className="fixed bottom-4 right-4">
{!isOpen ? (
<button
onClick={() => setIsOpen(true)}
className="w-12 h-12 bg-blue-500 text-white rounded-full shadow-lg hover:bg-blue-600 transition-colors"
>
💬
</button>
) : (
<div className="w-80 h-96 bg-white border rounded-lg shadow-lg flex flex-col">
<div className="p-4 border-b flex justify-between items-center">
<h3 className="font-semibold">Support Chat</h3>
<button
onClick={() => setIsOpen(false)}
className="text-gray-500 hover:text-gray-700"
>
✕
</button>
</div>
<div className="flex-1 p-4 overflow-y-auto">
{messages.map((message, index) => (
<div key={index} className={`mb-2 ${message.role === 'user' ? 'text-right' : 'text-left'}`}>
<div className={`inline-block p-2 rounded ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}>
{message.content}
</div>
</div>
))}
{isChatLoading && (
<div className="text-center text-gray-500">
<div className="animate-pulse">Typing...</div>
</div>
)}
</div>
<div className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
placeholder="Type your message..."
className="flex-1 p-2 border rounded"
onKeyPress={(e) => {
if (e.key === 'Enter') {
sendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
disabled={isChatLoading}
/>
<button
onClick={() => {
const input = document.querySelector('input');
if (input?.value) {
sendMessage(input.value);
input.value = '';
}
}}
disabled={isChatLoading}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:bg-gray-300"
>
Send
</button>
</div>
</div>
</div>
)}
</div>
);
};
export default SimpleChatWidget;E-commerce Support Chat
A more sophisticated chat for e-commerce with user context:
import React, { useState, useEffect } from 'react';
import { useChat } from '@rkt/rkt-chat-client';
interface EcommerceChatProps {
userId?: string;
orderId?: string;
productId?: string;
}
const EcommerceChat: React.FC<EcommerceChatProps> = ({ userId, orderId, productId }) => {
const [userContext, setUserContext] = useState<any>(null);
const {
sessionId,
isSessionReady,
telemetryInitialized,
messages,
sendMessage,
isChatLoading,
} = useChat({
chatbot: {
id: 'ecommerce-support',
prompt: `You are a helpful e-commerce support assistant.
User ID: ${userId || 'Guest'}
Order ID: ${orderId || 'None'}
Product ID: ${productId || 'None'}`,
},
baseUrl: 'https://api.example.com/chat',
telemetry: {
initializeSession: (data) => {
// Send to analytics
fetch('/api/analytics/session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...data,
userId,
orderId,
productId,
}),
});
},
trackEvent: (event, properties) => {
// Track user interactions
fetch('/api/analytics/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event,
properties: {
...properties,
userId,
sessionId,
},
}),
});
},
trackError: (error, context) => {
// Send to error tracking
console.error('Chat error:', error, context);
},
},
onReady: (sessionId) => {
// Load user context when session is ready
if (userId) {
fetch(`/api/users/${userId}/context`)
.then(res => res.json())
.then(setUserContext)
.catch(console.error);
}
},
});
const handleSendMessage = (message: string) => {
// Add user context to message
const enrichedMessage = userContext
? `${message}\n\nUser Context: ${JSON.stringify(userContext)}`
: message;
sendMessage(enrichedMessage);
};
if (!isSessionReady) {
return (
<div className="flex items-center justify-center p-8">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
<p>Initializing support chat...</p>
{telemetryInitialized && (
<p className="text-sm text-gray-500">Analytics ready</p>
)}
</div>
</div>
);
}
return (
<div className="max-w-4xl mx-auto p-6">
<div className="bg-white border rounded-lg shadow-lg">
<div className="p-4 border-b bg-gray-50">
<h2 className="text-xl font-semibold">Customer Support</h2>
<div className="text-sm text-gray-600 mt-1">
Session: {sessionId} | User: {userId || 'Guest'}
</div>
</div>
<div className="h-96 overflow-y-auto p-4">
{messages.map((message, index) => (
<div key={index} className={`mb-4 ${message.role === 'user' ? 'text-right' : 'text-left'}`}>
<div className={`inline-block max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}>
{message.content}
</div>
</div>
))}
{isChatLoading && (
<div className="text-center text-gray-500">
<div className="animate-pulse">Support agent is typing...</div>
</div>
)}
</div>
<div className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
placeholder="Describe your issue..."
className="flex-1 p-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
onKeyPress={(e) => {
if (e.key === 'Enter' && e.currentTarget.value.trim()) {
handleSendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
disabled={isChatLoading}
/>
<button
onClick={() => {
const input = document.querySelector('input');
if (input?.value.trim()) {
handleSendMessage(input.value);
input.value = '';
}
}}
disabled={isChatLoading}
className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 transition-colors"
>
Send
</button>
</div>
</div>
</div>
</div>
);
};
export default EcommerceChat;Advanced Examples
Multi-Tenant Chat System
A chat system that supports multiple tenants with different configurations:
import React, { useState, useEffect } from 'react';
import { useChat } from '@rkt/rkt-chat-client';
interface TenantConfig {
id: string;
name: string;
chatbotId: string;
apiUrl: string;
theme: 'light' | 'dark';
branding: {
primaryColor: string;
logo: string;
};
}
interface MultiTenantChatProps {
tenantId: string;
userId?: string;
}
const MultiTenantChat: React.FC<MultiTenantChatProps> = ({ tenantId, userId }) => {
const [tenantConfig, setTenantConfig] = useState<TenantConfig | null>(null);
const [isLoading, setIsLoading] = useState(true);
// Load tenant configuration
useEffect(() => {
fetch(`/api/tenants/${tenantId}/config`)
.then(res => res.json())
.then(setTenantConfig)
.catch(console.error)
.finally(() => setIsLoading(false));
}, [tenantId]);
const {
sessionId,
isSessionReady,
telemetryInitialized,
messages,
sendMessage,
isChatLoading,
} = useChat({
chatbot: {
id: tenantConfig?.chatbotId,
prompt: `You are a helpful assistant for ${tenantConfig?.name}.
Provide support and answer questions about their services.`,
},
baseUrl: tenantConfig?.apiUrl,
telemetry: {
initializeSession: (data) => {
// Send to tenant-specific analytics
fetch(`/api/tenants/${tenantId}/analytics/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...data,
tenantId,
userId,
}),
});
},
trackEvent: (event, properties) => {
fetch(`/api/tenants/${tenantId}/analytics/events`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event,
properties: {
...properties,
tenantId,
userId,
sessionId,
},
}),
});
},
trackError: (error, context) => {
// Send to tenant-specific error tracking
fetch(`/api/tenants/${tenantId}/errors`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: error.message,
stack: error.stack,
context: {
...context,
tenantId,
userId,
},
}),
});
},
},
onReady: (sessionId) => {
console.log(`Chat ready for tenant ${tenantId}:`, sessionId);
},
});
if (isLoading || !tenantConfig) {
return (
<div className="flex items-center justify-center p-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
</div>
);
}
if (!isSessionReady) {
return (
<div className="flex items-center justify-center p-8">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
<p>Initializing {tenantConfig.name} chat...</p>
</div>
</div>
);
}
return (
<div
className="max-w-4xl mx-auto p-6"
style={{
'--primary-color': tenantConfig.branding.primaryColor
} as React.CSSProperties}
>
<div className={`border rounded-lg shadow-lg ${
tenantConfig.theme === 'dark' ? 'bg-gray-800 text-white' : 'bg-white'
}`}>
<div className={`p-4 border-b ${
tenantConfig.theme === 'dark' ? 'bg-gray-700' : 'bg-gray-50'
}`}>
<div className="flex items-center gap-3">
<img
src={tenantConfig.branding.logo}
alt={tenantConfig.name}
className="w-8 h-8 rounded"
/>
<h2 className="text-xl font-semibold">{tenantConfig.name} Support</h2>
</div>
<div className="text-sm opacity-75 mt-1">
Session: {sessionId} | Tenant: {tenantId}
</div>
</div>
<div className="h-96 overflow-y-auto p-4">
{messages.map((message, index) => (
<div key={index} className={`mb-4 ${message.role === 'user' ? 'text-right' : 'text-left'}`}>
<div
className={`inline-block max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.role === 'user'
? 'text-white'
: tenantConfig.theme === 'dark'
? 'bg-gray-700 text-white'
: 'bg-gray-100 text-gray-800'
}`}
style={{
backgroundColor: message.role === 'user'
? tenantConfig.branding.primaryColor
: undefined
}}
>
{message.content}
</div>
</div>
))}
{isChatLoading && (
<div className="text-center opacity-75">
<div className="animate-pulse">Support agent is typing...</div>
</div>
)}
</div>
<div className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
placeholder="How can we help you?"
className={`flex-1 p-3 border rounded-lg focus:ring-2 focus:ring-opacity-50 ${
tenantConfig.theme === 'dark'
? 'bg-gray-700 border-gray-600 text-white focus:ring-white'
: 'bg-white border-gray-300 focus:ring-blue-500'
}`}
onKeyPress={(e) => {
if (e.key === 'Enter' && e.currentTarget.value.trim()) {
sendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
disabled={isChatLoading}
/>
<button
onClick={() => {
const input = document.querySelector('input');
if (input?.value.trim()) {
sendMessage(input.value);
input.value = '';
}
}}
disabled={isChatLoading}
className="px-6 py-3 text-white rounded-lg hover:opacity-90 disabled:opacity-50 transition-opacity"
style={{ backgroundColor: tenantConfig.branding.primaryColor }}
>
Send
</button>
</div>
</div>
</div>
</div>
);
};
export default MultiTenantChat;Real-time Collaboration Chat
A chat system with real-time collaboration features:
import React, { useState, useEffect, useRef } from 'react';
import { useChat } from '@rkt/rkt-chat-client';
interface CollaborationChatProps {
roomId: string;
userId: string;
userName: string;
}
const CollaborationChat: React.FC<CollaborationChatProps> = ({
roomId,
userId,
userName
}) => {
const [typingUsers, setTypingUsers] = useState<string[]>([]);
const [isTyping, setIsTyping] = useState(false);
const typingTimeoutRef = useRef<NodeJS.Timeout>();
const {
sessionId,
isSessionReady,
messages,
sendMessage,
isChatLoading,
} = useChat({
chatbot: {
id: 'collaboration-assistant',
prompt: `You are a helpful assistant in a collaborative workspace.
Room ID: ${roomId}
Current user: ${userName}
Help users collaborate effectively.`,
},
baseUrl: 'https://api.example.com/chat',
telemetry: {
initializeSession: (data) => {
// Join collaboration room
fetch('/api/collaboration/join', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...data,
roomId,
userId,
userName,
}),
});
},
trackEvent: (event, properties) => {
// Track collaboration events
fetch('/api/collaboration/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event,
properties: {
...properties,
roomId,
userId,
sessionId,
},
}),
});
},
trackError: (error, context) => {
console.error('Collaboration chat error:', error, context);
},
},
onReady: (sessionId) => {
// Set up real-time collaboration
const eventSource = new EventSource(`/api/collaboration/room/${roomId}/events`);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'user_typing':
setTypingUsers(prev => {
const filtered = prev.filter(user => user !== data.userName);
return [...filtered, data.userName];
});
break;
case 'user_stopped_typing':
setTypingUsers(prev => prev.filter(user => user !== data.userName));
break;
case 'message':
// Handle real-time messages if needed
break;
}
};
return () => eventSource.close();
},
});
const handleTyping = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (!isTyping) {
setIsTyping(true);
fetch('/api/collaboration/typing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomId,
userId,
userName,
isTyping: true,
}),
});
}
// Clear existing timeout
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
// Set new timeout to stop typing
typingTimeoutRef.current = setTimeout(() => {
setIsTyping(false);
fetch('/api/collaboration/typing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomId,
userId,
userName,
isTyping: false,
}),
});
}, 1000);
};
const handleSendMessage = (message: string) => {
// Stop typing indicator
setIsTyping(false);
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
sendMessage(message);
};
if (!isSessionReady) {
return (
<div className="flex items-center justify-center p-8">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
<p>Joining collaboration room...</p>
</div>
</div>
);
}
return (
<div className="max-w-4xl mx-auto p-6">
<div className="bg-white border rounded-lg shadow-lg">
<div className="p-4 border-b bg-gray-50">
<h2 className="text-xl font-semibold">Collaboration Chat</h2>
<div className="text-sm text-gray-600 mt-1">
Room: {roomId} | User: {userName} | Session: {sessionId}
</div>
</div>
<div className="h-96 overflow-y-auto p-4">
{messages.map((message, index) => (
<div key={index} className={`mb-4 ${message.role === 'user' ? 'text-right' : 'text-left'}`}>
<div className={`inline-block max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}>
{message.content}
</div>
</div>
))}
{typingUsers.length > 0 && (
<div className="text-sm text-gray-500 italic">
{typingUsers.join(', ')} {typingUsers.length === 1 ? 'is' : 'are'} typing...
</div>
)}
{isChatLoading && (
<div className="text-center text-gray-500">
<div className="animate-pulse">AI is responding...</div>
</div>
)}
</div>
<div className="p-4 border-t">
<div className="flex gap-2">
<input
type="text"
placeholder="Type your message..."
className="flex-1 p-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
onKeyPress={(e) => {
if (e.key === 'Enter' && e.currentTarget.value.trim()) {
handleSendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
onKeyDown={handleTyping}
disabled={isChatLoading}
/>
<button
onClick={() => {
const input = document.querySelector('input');
if (input?.value.trim()) {
handleSendMessage(input.value);
input.value = '';
}
}}
disabled={isChatLoading}
className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:bg-gray-300 transition-colors"
>
Send
</button>
</div>
</div>
</div>
</div>
);
};
export default CollaborationChat;Best Practices
Error Handling
Always implement comprehensive error handling:
const useChatWithErrorHandling = (options) => {
const [error, setError] = useState<string | null>(null);
const [retryCount, setRetryCount] = useState(0);
const chat = useChat({
...options,
onError: (error) => {
setError(error.message);
console.error('Chat error:', error);
// Implement retry logic
if (retryCount < 3) {
setTimeout(() => {
setRetryCount(prev => prev + 1);
setError(null);
}, 1000 * Math.pow(2, retryCount));
}
},
});
return {
...chat,
error,
retryCount,
clearError: () => setError(null),
};
};Performance Optimization
Optimize for performance with proper memoization:
const OptimizedChatComponent = React.memo(({ chatbotId, apiUrl }) => {
const chat = useChat({
chatbot: { id: chatbotId },
baseUrl: apiUrl,
});
const handleSendMessage = useCallback((message: string) => {
chat.sendMessage(message);
}, [chat.sendMessage]);
return (
<div>
{/* Chat UI */}
</div>
);
});Accessibility
Ensure your chat is accessible:
const AccessibleChat = () => {
const chat = useChat(options);
const [announcements, setAnnouncements] = useState<string[]>([]);
const announceMessage = useCallback((message: string) => {
setAnnouncements(prev => [...prev, message]);
// Clear announcement after a delay
setTimeout(() => {
setAnnouncements(prev => prev.slice(1));
}, 3000);
}, []);
useEffect(() => {
if (chat.messages.length > 0) {
const lastMessage = chat.messages[chat.messages.length - 1];
if (lastMessage.role === 'assistant') {
announceMessage(`New message: ${lastMessage.content}`);
}
}
}, [chat.messages, announceMessage]);
return (
<div>
{/* Screen reader announcements */}
<div aria-live="polite" aria-atomic="true" className="sr-only">
{announcements.map((announcement, index) => (
<div key={index}>{announcement}</div>
))}
</div>
{/* Chat UI with proper ARIA labels */}
<div role="log" aria-label="Chat messages">
{chat.messages.map((message, index) => (
<div key={index} role="article" aria-label={`Message from ${message.role}`}>
{message.content}
</div>
))}
</div>
</div>
);
};Security Considerations
Implement proper security measures:
const SecureChat = () => {
const [csrfToken, setCsrfToken] = useState<string>('');
useEffect(() => {
// Get CSRF token
fetch('/api/csrf-token')
.then(res => res.json())
.then(data => setCsrfToken(data.token));
}, []);
const chat = useChat({
chatbot: { id: 'secure-chatbot' },
baseUrl: 'https://api.example.com/chat',
// Add security headers
headers: {
'X-CSRF-Token': csrfToken,
'Authorization': `Bearer ${localStorage.getItem('authToken')}`,
},
});
// Sanitize user input
const sanitizeInput = (input: string) => {
return input.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
};
const handleSendMessage = (message: string) => {
const sanitizedMessage = sanitizeInput(message);
chat.sendMessage(sanitizedMessage);
};
return (
<div>
{/* Secure chat UI */}
</div>
);
};Testing Examples
Unit Tests
import { renderHook, act } from '@testing-library/react';
import { useChat } from '@rkt/rkt-chat-client';
describe('useChat Session Initialization', () => {
it('should initialize session correctly', async () => {
const { result } = renderHook(() => useChat({
chatbot: { id: 'test-chatbot' },
baseUrl: 'https://api.example.com/chat',
}));
// Wait for session initialization
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 100));
});
expect(result.current.sessionId).toBeTruthy();
expect(result.current.isSessionReady).toBe(true);
});
it('should handle telemetry initialization', async () => {
const mockTelemetry = {
initializeSession: jest.fn(),
trackEvent: jest.fn(),
trackError: jest.fn(),
};
const { result } = renderHook(() => useChat({
chatbot: { id: 'test-chatbot' },
baseUrl: 'https://api.example.com/chat',
telemetry: mockTelemetry,
}));
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 100));
});
expect(mockTelemetry.initializeSession).toHaveBeenCalled();
expect(result.current.telemetryInitialized).toBe(true);
});
});Integration Tests
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import ChatComponent from './ChatComponent';
describe('ChatComponent Integration', () => {
it('should send messages and receive responses', async () => {
render(<ChatComponent chatbotId="test-chatbot" apiUrl="https://api.example.com/chat" />);
// Wait for session initialization
await waitFor(() => {
expect(screen.getByText(/Session ID:/)).toBeInTheDocument();
});
// Send a message
const input = screen.getByPlaceholderText(/Type your message/);
const sendButton = screen.getByText('Send');
fireEvent.change(input, { target: { value: 'Hello, AI!' } });
fireEvent.click(sendButton);
// Verify message was sent
await waitFor(() => {
expect(screen.getByText('Hello, AI!')).toBeInTheDocument();
});
});
});Next Steps
- API Reference - Complete API documentation
- Integration Guide - Step-by-step integration
- Troubleshooting - Common issues and solutions
Last updated on