A React TypeScript chat widget component with floating button, theming, and Blumessage API integration. Features real-time messaging, conversation persistence, error handling, and a modern UI built with Tailwind CSS.
- 🎈 Floating Chat Widget - Appears as a button, expands to full chat
- 🎨 Light/Dark Themes - Built-in theme support
- 💾 Persistent Storage - Save conversations to localStorage or sessionStorage
- 🔄 Real-time Messaging - Instant API integration with Blumessage
- 📱 Responsive Design - Works on desktop and mobile
- 🎯 Flexible Icons - Supports various icon patterns
- ⚡ Error Handling - Comprehensive error callbacks
- 🔧 Highly Customizable - Extensive prop options
npm i @blumessage/react-chat
For the simplest setup with everything included:
<!-- Load the Blumessage Chat component (includes React) -->
<script src="path/to/index.js"></script>
For when you already have React loaded:
<!-- Load React and ReactDOM from CDN -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- Load the Blumessage Chat component -->
<script src="path/to/blumessage-chat.browser.js"></script>
See BROWSER_USAGE.md for complete browser usage documentation.
import React from 'react';
import { BlumessageChat } from '@blumessage/react-chat';
function App() {
return (
<BlumessageChat
apiKey="your-blumessage-api-key"
/>
);
}
import React from 'react';
import { BlumessageChat } from '@blumessage/react-chat';
function App() {
return (
<BlumessageChat
apiKey="your-blumessage-api-key"
floating={false}
width="400px"
height="600px"
/>
);
}
Prop | Type | Default | Description |
---|---|---|---|
Required | |||
apiKey |
string |
- | Your Blumessage API key |
Display & Content | |||
name |
string |
"Blumessage AI" |
Display name for the AI assistant |
subtitle |
string |
"Online • Instant responses" |
Subtitle shown under the name |
placeholder |
string |
"Type your message..." |
Input placeholder text |
theme |
'light' | 'dark' |
'light' |
Chat widget theme |
primaryColor |
string |
"linear-gradient(to right, #3b82f6,rgb(8, 98, 242))" |
Primary color/gradient |
Layout & Sizing | |||
floating |
boolean |
true |
Show as floating widget vs embedded |
width |
string |
'380px' (medium) |
Custom width (overrides size) |
height |
string |
'500px' (medium) |
Custom height (overrides size) |
size |
'small' | 'medium' | 'large' |
'medium' |
Predefined size preset |
fullScreen |
boolean |
false |
Force full screen mode |
Floating Widget Options | |||
buttonText |
string |
"Chat with us" |
Text on floating button |
buttonPosition |
'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' |
'bottom-right' |
Button position |
buttonStyle |
React.CSSProperties |
- | Custom button styles |
defaultOpen |
boolean |
false |
Start with chat open |
maximizeToggleButton |
boolean |
true |
Show maximize/minimize button |
icon |
string |
'message-circle' |
Icon for button (flexible naming) |
Help Bubble Options | |||
showHelpBubble |
boolean |
true |
Show help bubble above floating button |
helpBubbleMessage |
string |
"👋 If you need help chat with us" |
Message text in help bubble |
helpBubbleIcon |
string |
'hand' |
Icon for help bubble (supports lucide-react icon names) |
helpBubbleShowAfter |
number |
1 |
Seconds to wait before showing bubble |
helpBubbleHideAfter |
number |
5 |
Seconds to wait before hiding bubble (0 = never hide) |
Messages & Data | |||
initialMessages |
Message[] |
[] |
Pre-populate with messages |
conversationId |
string |
- | Continue specific conversation |
persistent |
boolean |
false |
Use localStorage vs sessionStorage |
showTimestamps |
boolean |
false |
Display timestamps: today="2:00 PM", older="17 July, 1:00 PM" |
typingText |
string |
"Agent is typing..." |
Custom text shown while waiting for assistant response |
emptyStateText |
string |
"Start a conversation!" |
Text shown when no messages are present |
markdown |
boolean |
true |
Enable markdown rendering for messages |
disableAutoScroll |
boolean |
false |
Disable automatic scrolling to bottom when new messages arrive |
Event Callbacks | |||
onUserMessage |
(message: Message) => void |
- | Called when user sends message |
onAssistantMessage |
(message: Message) => void |
- | Called when assistant responds |
onConversationIdChange |
(id: string | null) => void |
- | Called when conversation ID changes |
onChatWidgetOpen |
() => void |
- | Called when floating chat opens |
onChatWidgetClosed |
() => void |
- | Called when floating chat closes |
onError |
(error: string, context?: string) => void |
- | Called on any error |
<BlumessageChat
apiKey="your-api-key"
theme="dark"
name="Support Bot"
onUserMessage={(message) => console.log('User:', message.content)}
onAssistantMessage={(message) => console.log('Bot:', message.content)}
onError={(error, context) => console.error(`Error in ${context}:`, error)}
/>
<BlumessageChat
apiKey="your-api-key"
persistent={true} // Saves to localStorage instead of sessionStorage
onConversationIdChange={(id) => {
// Optionally save ID to your own storage
if (id) localStorage.setItem('my-chat-id', id);
}}
/>
<BlumessageChat
apiKey="your-api-key"
size="large"
primaryColor="linear-gradient(45deg, #ff6b6b, #4ecdc4)"
buttonPosition="bottom-left"
buttonText="Need Help?"
icon="headphones"
maximizeToggleButton={true}
/>
const initialMessages = [
{
id: '1',
role: 'assistant' as const,
content: 'Hello! How can I help you today?',
timestamp: Date.now()
}
];
<BlumessageChat
apiKey="your-api-key"
floating={false}
width="100%"
height="500px"
initialMessages={initialMessages}
placeholder="Ask me anything..."
/>
<BlumessageChat
apiKey="your-api-key"
floating={true}
showHelpBubble={true}
helpBubbleMessage="👋 Need assistance? We're here to help!"
helpBubbleIcon="hand" // Use hand icon (default)
helpBubbleShowAfter={3} // Show after 3 seconds
helpBubbleHideAfter={10} // Hide after 10 seconds
/>
<BlumessageChat
apiKey="your-api-key"
floating={true}
showHelpBubble={true}
helpBubbleIcon="star" // Use star icon
helpBubbleMessage="⭐ Rate our service!"
helpBubbleShowAfter={2}
helpBubbleHideAfter={8}
/>
<BlumessageChat
apiKey="your-api-key"
floating={true}
showHelpBubble={true}
helpBubbleIcon="lightbulb" // Use lightbulb icon
helpBubbleMessage="💡 Got an idea? Let's discuss it!"
helpBubbleShowAfter={1}
helpBubbleHideAfter={0} // Never hide
/>
<BlumessageChat
apiKey="your-api-key"
floating={true}
showHelpBubble={true}
helpBubbleIcon="help-circle" // Use help icon
helpBubbleMessage="❓ Questions? We're here to help!"
helpBubbleShowAfter={5}
helpBubbleHideAfter={15}
/>
### Persistent Help Bubble (Never Hide)
```tsx
<BlumessageChat
apiKey="your-api-key"
floating={true}
showHelpBubble={true}
helpBubbleMessage="💬 Questions? Click to chat with us!"
helpBubbleShowAfter={2} // Show after 2 seconds
helpBubbleHideAfter={0} // Never hide (0 = persistent)
/>
<BlumessageChat
apiKey="your-api-key"
showTimestamps={true} // Shows "2:00 PM" or "17 July, 1:00 PM"
floating={false}
initialMessages={[
{
id: '1',
role: 'assistant' as const,
content: 'Message from today',
timestamp: Date.now() - (60 * 60 * 1000) // Shows: "2:00 PM" (or "14:00")
},
{
id: '2',
role: 'user' as const,
content: 'Message from yesterday',
timestamp: Date.now() - (24 * 60 * 60 * 1000) // Shows: "17 July, 1:00 PM"
}
]}
/>
<BlumessageChat
apiKey="your-api-key"
typingText="Support agent is typing..." // Custom loading message
// Or use different text for different languages:
// typingText="L'agent écrit..." // French
// typingText="El agente está escribiendo..." // Spanish
/>
<BlumessageChat
apiKey="your-api-key"
emptyStateText="Welcome! How can I help you today?" // Custom empty state message
// Or use different text for different contexts:
// emptyStateText="Ask me anything about our products..."
// emptyStateText="Ready to assist with your questions!"
/>
<BlumessageChat
apiKey="your-api-key"
disableAutoScroll={true} // Prevents automatic scrolling to bottom
// Useful when you want users to maintain their scroll position
// or when implementing custom scroll behavior
/>
The icon
prop accepts any lucide-react icon name with flexible naming patterns. The component intelligently matches your input to the appropriate lucide-react icon:
// ✅ Any lucide-react icon works:
icon="message-circle" // MessageCircle
icon="phone" // Phone
icon="mail" // Mail
icon="headphones" // Headphones
icon="bot" // Bot
icon="users" // Users
icon="heart" // Heart
icon="star" // Star
icon="zap" // Zap
icon="settings" // Settings
icon="help-circle" // HelpCircle
icon="info" // Info
icon="alert-triangle" // AlertTriangle
// ✅ Flexible naming patterns (case-insensitive, ignores hyphens/underscores):
icon="MessageCircle" // → MessageCircle
icon="message_circle" // → MessageCircle
icon="message circle" // → MessageCircle
icon="chat" // → MessageCircle (smart matching)
icon="messaging" // → MessageCircle (smart matching)
icon="support" // → Headphones (smart matching)
icon="email" // → Mail (smart matching)
icon="call" // → Phone (smart matching)
// ✅ Default fallback: MessageCircle (if no match found)
icon="unknown-icon" // → MessageCircle
Browse all available icons at: lucide.dev/icons
Simply use the icon name from lucide-react, and the component will handle the rest!
The onError
callback provides detailed error context:
<BlumessageChat
apiKey="your-api-key"
onError={(error, context) => {
switch(context) {
case 'missing_api_key':
// Handle missing API key
break;
case 'api_key_validation':
// Handle invalid API key
break;
case 'network_error':
// Handle network issues
break;
case 'conversation_history':
// Handle history fetch errors
break;
case 'message_send':
// Handle message sending errors
break;
}
}}
/>
The component supports external message submission through a ref, allowing parent components to programmatically send messages and control the chat widget.
import React, { useRef } from 'react';
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
function App() {
const chatRef = useRef<BlumessageChatRef>(null);
const sendExternalMessage = async () => {
if (chatRef.current) {
await chatRef.current.sendMessage("Hello from external component!");
}
};
const openChat = () => {
chatRef.current?.openChat();
};
const closeChat = () => {
chatRef.current?.closeChat();
};
const clearChat = () => {
chatRef.current?.clearConversation();
};
const getMessages = () => {
const messages = chatRef.current?.getMessages();
console.log('Current messages:', messages);
};
const getToken = () => {
const token = chatRef.current?.getToken();
console.log('Current conversation token:', token);
};
return (
<div>
<button onClick={sendExternalMessage}>Send External Message</button>
<button onClick={openChat}>Open Chat</button>
<button onClick={closeChat}>Close Chat</button>
<button onClick={clearChat}>Clear Chat</button>
<button onClick={getMessages}>Get Messages</button>
<button onClick={getToken}>Get Token</button>
<BlumessageChat
ref={chatRef}
apiKey="your-api-key"
floating={true}
/>
</div>
);
}
Method | Type | Description |
---|---|---|
sendMessage(message: string) |
Promise<void> |
Send a message programmatically |
openChat() |
void |
Open the floating chat widget |
closeChat() |
void |
Close the floating chat widget |
clearConversation() |
void |
Clear all messages and reset conversation |
getMessages() |
Message[] |
Get current messages array |
getToken() |
string | null |
Get current conversation token |
import React, { useRef, useState } from 'react';
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
function App() {
const [externalMessage, setExternalMessage] = useState('');
const chatRef = useRef<BlumessageChatRef>(null);
const handleSendExternalMessage = async () => {
if (!externalMessage.trim() || !chatRef.current) return;
try {
await chatRef.current.sendMessage(externalMessage);
setExternalMessage('');
console.log('External message sent successfully');
} catch (error) {
console.error('Failed to send external message:', error);
}
};
return (
<div>
<div style={{ marginBottom: '20px' }}>
<input
type="text"
value={externalMessage}
onChange={(e) => setExternalMessage(e.target.value)}
placeholder="Type a message to send externally..."
onKeyPress={(e) => e.key === 'Enter' && handleSendExternalMessage()}
/>
<button onClick={handleSendExternalMessage}>Send</button>
<button onClick={() => chatRef.current?.openChat()}>Open Chat</button>
<button onClick={() => chatRef.current?.closeChat()}>Close Chat</button>
<button onClick={() => chatRef.current?.clearConversation()}>Clear Chat</button>
</div>
<BlumessageChat
ref={chatRef}
apiKey="your-api-key"
floating={true}
onUserMessage={(message) => console.log('User message:', message)}
onAssistantMessage={(message) => console.log('Assistant message:', message)}
/>
</div>
);
}
Full TypeScript definitions included:
import { BlumessageChat, Message, BlumessageChatProps, BlumessageChatRef } from '@blumessage/react-chat';
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: number;
}
interface BlumessageChatRef {
sendMessage: (message: string) => Promise<void>;
openChat: () => void;
closeChat: () => void;
clearConversation: () => void;
getMessages: () => Message[];
getToken: () => string | null;
}
- SessionStorage (default): Conversations persist until browser tab closes
- LocalStorage (persistent=true): Conversations persist across browser sessions
- Automatic cleanup: Invalid conversation IDs are automatically cleared
- History restoration: Previous conversations load automatically on component mount
- Modern browsers with ES2017+ support
- React 18+
- TypeScript 4.5+
npm run build
npm run build:commonjs
This creates dist/index.js
which includes React and can be used directly in browsers.
npm run build:browser
This creates dist/blumessage-chat.browser.js
which requires React to be loaded separately.
UNLICENSED - For use only by customers with an active Blumessage subscription.
For feature requests, technical support and assistance, please contact us at: contact@blumessage.com