Framework Integration
Using Core with React
Getting started
Chat Widget
- Getting Started
- Core Integration
- Core Package Deep Dive
- Framework Integration
- Customization
- Reference
AI Phone Support
AI WhatsApp Support
AI SMS Support
AI Email Support
Training your AI
Native Integrations
AI Safety
Framework Integration
Using Core with React
Learn how to integrate the OpenCX Widget Core Package with React applications
Using Core with React
The OpenCX Widget provides two approaches for React integration:
- Using the React package (
@opencx/widget/react
) for quick integration - Using the core package directly for more control
Quick Integration with React Package
import { WidgetRoot } from '@opencx/widget/react';
function App() {
return (
<WidgetRoot
config={{
token: 'YOUR_BOT_TOKEN',
apiUrl: 'YOUR_API_URL',
user: {
email: "user@example.com",
name: "User Name"
}
}}
>
<YourChatInterface />
</WidgetRoot>
);
}
Available Hooks
// Main chat functionality
const { messages, sendMessage, loading, error } = useChat();
// Configuration access
const { config, updateConfig } = useConfigData();
// Localization
const { t, locale, setLocale } = useLocale();
// Sound effects
const { playMessageSound, playNotificationSound } = useWidgetSoundEffects();
// Contact management
const { contact, updateContact } = useContact();
// Message feedback
const [upvoteState, upvote] = useUpvote(messageId);
const [downvoteState, downvote] = useDownvote(messageId);
Custom Integration with Core Package
For more control over the implementation, you can use the core package directly:
import {
createChat,
createConfig,
ApiCaller,
Platform,
type MessageType
} from '@opencx/widget';
import { useState, useEffect, createContext, useContext } from 'react';
// Create context for chat functionality
const ChatContext = createContext<{
messages: MessageType[];
sendMessage: (content: string) => Promise<void>;
loading: boolean;
error: Error | null;
} | null>(null);
// Chat provider component
export function ChatProvider({ children }: { children: React.ReactNode }) {
const [messages, setMessages] = useState<MessageType[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
// Initialize chat
const config = createConfig({
token: process.env.REACT_APP_BOT_TOKEN,
apiUrl: process.env.REACT_APP_API_URL,
user: {
email: "user@example.com",
name: "User Name"
}
});
const api = new ApiCaller({ config: config.getConfig() });
const platform: Platform = {
env: { platform: 'web' },
storage: {
getItem: async (key) => localStorage.getItem(key),
setItem: async (key, value) => localStorage.setItem(key, value),
removeItem: async (key) => localStorage.removeItem(key)
},
logger: {
level: 'debug',
prefix: '[OpenChat]',
enabled: true
}
};
const chat = createChat({ api, config, platform });
// Subscribe to chat state
const unsubscribe = chat.chatState.subscribe((state) => {
setLoading(state.loading.isLoading);
if (state.error.hasError) {
setError(new Error(state.error.message));
return;
}
setMessages(state.messages);
});
return () => {
unsubscribe();
};
}, []);
return (
<ChatContext.Provider value={{
messages,
sendMessage: async (content) => {
// Implementation of send message
},
loading,
error
}}>
{children}
</ChatContext.Provider>
);
}
// Custom hook for using chat
export function useChat() {
const context = useContext(ChatContext);
if (!context) {
throw new Error('useChat must be used within a ChatProvider');
}
return context;
}
Component Examples
Chat Container
function ChatContainer() {
const { messages, loading, error } = useChat();
if (error) {
return <ErrorDisplay error={error} />;
}
return (
<div className="chat-container">
{loading && <LoadingIndicator />}
<MessageList messages={messages} />
<InputArea />
</div>
);
}
Message List with Virtual Scrolling
import { FixedSizeList } from 'react-window';
function MessageList({ messages }: { messages: MessageType[] }) {
const listRef = useRef<FixedSizeList>(null);
useEffect(() => {
listRef.current?.scrollToItem(messages.length - 1);
}, [messages.length]);
const Row = ({ index, style }: { index: number, style: any }) => (
<Message
message={messages[index]}
style={style}
/>
);
return (
<FixedSizeList
ref={listRef}
height={400}
itemCount={messages.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
Message Component
function Message({ message, style }: { message: MessageType, style?: any }) {
const messageClass = message.type === 'FROM_USER' ? 'user-message' : 'bot-message';
return (
<div className={`message ${messageClass}`} style={style}>
{message.type === 'FROM_BOT' ? (
<BotMessageContent message={message as BotMessageType<{ text: string }>} />
) : (
<UserMessageContent message={message} />
)}
<MessageFeedback messageId={message.id} />
</div>
);
}
Message Feedback
function MessageFeedback({ messageId }: { messageId: string }) {
const [upvoteState, upvote] = useUpvote(messageId);
const [downvoteState, downvote] = useDownvote(messageId);
return (
<div className="message-feedback">
<button
onClick={upvote}
disabled={upvoteState.loading}
aria-label="Helpful"
>
👍
</button>
<button
onClick={downvote}
disabled={downvoteState.loading}
aria-label="Not helpful"
>
👎
</button>
</div>
);
}
Input Area
function InputArea() {
const { sendMessage } = useChat();
const [input, setInput] = useState('');
const [sending, setSending] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) return;
setSending(true);
try {
await sendMessage(input);
setInput('');
} catch (error) {
console.error('Failed to send message:', error);
} finally {
setSending(false);
}
};
return (
<form onSubmit={handleSubmit} className="input-area">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
disabled={sending}
aria-label="Message input"
/>
<button
type="submit"
disabled={sending || !input.trim()}
aria-label={sending ? 'Sending message' : 'Send message'}
>
{sending ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Error Boundaries
class ChatErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Chat error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="error-container" role="alert">
<h2>Something went wrong</h2>
<button
onClick={() => this.setState({ hasError: false })}
aria-label="Try again"
>
Try again
</button>
</div>
);
}
return this.props.children;
}
}
Best Practices
-
State Management
- Use React Context for global chat state
- Implement proper cleanup in useEffect
- Handle component unmounting
-
Performance
- Use virtual scrolling for long message lists
- Memoize callbacks and heavy computations
- Implement proper error boundaries
-
Accessibility
- Add proper ARIA labels
- Handle keyboard navigation
- Provide focus management
-
Testing
- Mock chat service for tests
- Test error scenarios
- Verify state updates
-
Error Handling
- Use error boundaries
- Provide clear error messages
- Implement retry mechanisms
Was this page helpful?