miia-chat-widget
v1.2.0
Published
A professional, customizable React chat widget component for embedding AI chat interfaces
Maintainers
Readme
Miia Chat Widget
A professional, customizable React chat widget component for embedding AI chat interfaces in your web applications.
Features
- 🎨 Beautiful UI - Modern, polished design inspired by Miia
- 🔄 Dual Modes - Floating button or embedded chat
- 🌍 i18n Support - Built-in support for 5 languages (EN, ES, FR, DE, PT)
- 📝 Markdown Rendering - Full markdown support for rich messages
- 💾 Chat History - Automatic persistence via localStorage with unique keys per instance
- 🔌 Plugin System - Extensible architecture for custom functionality
- 🎭 Fully Customizable - Theme colors, avatars, messages, and more
- 📱 Responsive - Works perfectly on mobile and desktop
- ⚡ TypeScript - Fully typed for excellent DX
- 🎯 Demo Mode - Test without backend integration
- 🛡️ CSS Isolation - Styles are scoped to prevent conflicts with host applications
Installation
npm install miia-chat-widgetor
yarn add miia-chat-widgetor
pnpm add miia-chat-widgetQuick Start
import { ChatWidget } from 'miia-chat-widget';
import 'miia-chat-widget/dist/style.css';
function App() {
return (
<ChatWidget
apiEndpoint="https://api.example.com/chat"
apiKey="your-api-key"
welcomeMessage="👋 Hi! How can I help you today?"
suggestedActions={[
'What is your product?',
'Show me pricing',
'Contact support',
]}
botName="Support Bot"
theme={{
primaryColor: '#000000',
accentColor: '#3b82f6',
}}
/>
);
}Display Modes
Floating Mode (Default)
A floating button appears in the corner of your page. Click to open/close the chat.
<ChatWidget
mode="floating"
position="bottom-right" // or "bottom-left", "top-right", "top-left"
apiEndpoint="https://api.example.com/chat"
/>Embedded Mode
The chat is always visible and fills its container.
<div style={{ width: '400px', height: '600px' }}>
<ChatWidget
mode="embedded"
apiEndpoint="https://api.example.com/chat"
/>
</div>Props API
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| mode | 'floating' \| 'embedded' | 'floating' | Display mode |
| position | FloatingPosition | 'bottom-right' | Position for floating mode |
| apiEndpoint | string | - | Your chat API endpoint |
| apiKey | string | - | API authentication key |
| theme | ChatWidgetTheme | - | Custom theme colors |
| welcomeMessage | string | - | Initial bot message |
| placeholder | string | 'Message...' | Input placeholder text |
| suggestedActions | string[] | [] | Quick action buttons |
| botName | string | 'Miia AI Agent' | Bot display name |
| avatar | string | - | Bot avatar URL |
| plugins | ChatPlugin[] | [] | Custom plugins |
| customHeaders | Record<string, string> | - | Additional API headers |
| enableMarkdown | boolean | true | Enable markdown rendering |
| enableHistory | boolean | true | Persist chat history |
| historyKey | string | auto-generated | localStorage key for history. If not provided, a unique key is automatically generated per widget instance |
| sessionId | string | auto-generated | Unique session identifier sent to the server. If not provided, a unique ID is automatically generated and persisted in localStorage |
| timeout | number | 60000 | Request timeout in milliseconds. Default is 60 seconds (60000ms), suitable for LLM processing. Increase for slower APIs |
| demoMode | boolean | false | Use simulated responses |
| onMessageSent | (message: string) => void | - | Callback when user sends message |
| onMessageReceived | (response: ChatMessage) => void | - | Callback when bot responds |
| onError | (error: Error) => void | - | Error callback |
| onOpen | () => void | - | Callback when chat opens (floating mode) |
| onClose | () => void | - | Callback when chat closes (floating mode) |
| locale | string | 'en' | Language code |
| translations | LocaleData | - | Custom translations |
| maxHeight | string | '600px' | Maximum height |
| maxWidth | string | '384px' | Maximum width |
Theming
Customize the appearance to match your brand:
<ChatWidget
theme={{
primaryColor: '#7c3aed', // Purple
backgroundColor: '#ffffff', // White
accentColor: '#f59e0b', // Amber
borderRadius: '12px', // Rounded corners
fontFamily: 'Inter, sans-serif'
}}
/>Plugin System
Extend functionality with plugins:
import { ChatPlugin } from 'miia-chat-widget';
// Profanity filter plugin
const profanityFilter: ChatPlugin = {
name: 'profanity-filter',
onBeforeMessageSent: async (message) => {
return message.replace(/badword/gi, '***');
},
};
// Analytics plugin
const analytics: ChatPlugin = {
name: 'analytics',
onInit: async () => {
console.log('Analytics initialized');
},
onAfterMessageReceived: async (response) => {
// Track bot response
trackEvent('bot_response', { messageId: response.id });
return response;
},
onError: (error) => {
// Track errors
trackError(error);
},
};
// Use plugins
<ChatWidget
plugins={[profanityFilter, analytics]}
apiEndpoint="https://api.example.com/chat"
/>Plugin Interface
interface ChatPlugin {
name: string;
onInit?: () => void | Promise<void>;
onBeforeMessageSent?: (message: string) => string | Promise<string>;
onAfterMessageReceived?: (response: ChatMessage) => ChatMessage | Promise<ChatMessage>;
onError?: (error: Error) => void;
}Internationalization (i18n)
Built-in support for multiple languages:
// Use a built-in language
<ChatWidget locale="es" /> // Spanish
<ChatWidget locale="fr" /> // French
<ChatWidget locale="de" /> // German
<ChatWidget locale="pt" /> // Portuguese
// Add custom translations
<ChatWidget
locale="es"
translations={{
es: {
placeholder: 'Escribe un mensaje...',
send: 'Enviar',
poweredBy: 'Desarrollado por',
},
}}
/>Supported Languages
- English (
en) - Default - Spanish (
es) - French (
fr) - German (
de) - Portuguese (
pt)
API Integration
Your API endpoint should accept POST requests with this format:
Request:
{
"message": "User's message text",
"sessionId": "unique-session-identifier"
}Note: The
sessionIdis automatically generated and persisted in localStorage. It allows your server to maintain conversation history for each user. You can also provide a customsessionIdvia the widget props.
Response:
{
"message": "Bot's response text",
// Alternative keys: "response", "content", "text"
}Example Backend (Node.js/Express)
app.post('/chat', async (req, res) => {
const { message, sessionId } = req.body;
// Use sessionId to retrieve/store conversation history
const conversationHistory = await getHistory(sessionId);
// Process with your AI/backend
const response = await processMessage(message, conversationHistory);
// Save the conversation
await saveHistory(sessionId, { message, response });
res.json({
message: response,
});
});Demo Mode
Test the widget without a backend:
<ChatWidget
demoMode={true}
welcomeMessage="👋 Hi! I'm in demo mode."
suggestedActions={['Tell me a joke', 'What can you do?']}
/>Demo mode automatically activates if no apiEndpoint is provided.
Chat History
History is automatically saved to localStorage and restored on page reload. Each widget instance automatically gets a unique storage key to prevent conflicts:
<ChatWidget
enableHistory={true}
// historyKey is auto-generated if not provided
// Format: "miia-chat-history-{endpoint-hash}-{instance-id}"
/>Custom History Key
If you want to share history between instances or use a custom key:
<ChatWidget
enableHistory={true}
historyKey="my-custom-key" // Custom storage key
/>Disable History
<ChatWidget
enableHistory={false} // Disable history persistence
/>Note: When historyKey is not provided, each widget instance automatically generates a unique key based on the apiEndpoint (if available) and a unique instance ID. This ensures that multiple widgets on the same page or different sites don't share chat history.
Session Management
The widget automatically manages a unique session identifier (sessionId) that is sent with each API request. This allows your server to maintain conversation context across messages.
Automatic Session ID
By default, a unique sessionId is automatically generated and persisted in localStorage:
<ChatWidget
apiEndpoint="https://api.example.com/chat"
// sessionId is auto-generated and persisted
// Format: "1234567890-abc123def"
/>The sessionId persists across page reloads, allowing the server to maintain conversation history for returning users.
Custom Session ID
You can provide a custom sessionId to integrate with your own user authentication or session management:
function App() {
const [userId, setUserId] = useState(null);
useEffect(() => {
// Get user ID from your auth system
const user = getCurrentUser();
setUserId(user?.id);
}, []);
return (
<ChatWidget
apiEndpoint="https://api.example.com/chat"
sessionId={userId} // Use your own session identifier
/>
);
}This is useful when:
- You want to link chat sessions to authenticated users
- You need to sync sessions across multiple devices
- You have your own session management system
Note: If you provide a custom sessionId, it will override the automatic one, and the widget will not persist it in localStorage.
Request Timeout
By default, the widget waits up to 60 seconds (60000ms) for the server to respond. This is suitable for most LLM APIs that may take time to process requests.
Custom Timeout
You can configure a custom timeout for slower APIs or longer processing times:
<ChatWidget
apiEndpoint="https://api.example.com/chat"
timeout={120000} // 2 minutes (120 seconds)
/>Timeout Examples
// Default: 60 seconds (suitable for most LLMs)
<ChatWidget apiEndpoint="..." />
// Fast API: 30 seconds
<ChatWidget apiEndpoint="..." timeout={30000} />
// Slow LLM: 2 minutes
<ChatWidget apiEndpoint="..." timeout={120000} />
// Very slow processing: 5 minutes
<ChatWidget apiEndpoint="..." timeout={300000} />If the timeout is exceeded, the widget will display an error message: "Request timeout after Xms. The server took too long to respond."
Markdown Support
Messages support full markdown syntax:
- Bold, italic, ~~strikethrough~~
- Links: text
- Lists (ordered and unordered)
- Code blocks
- Blockquotes
- Headings
<ChatWidget
enableMarkdown={true}
demoMode={true}
/>
// Bot can respond with:
// "Here's some **bold text** and a [link](https://example.com)"Advanced Examples
Custom Event Handling
<ChatWidget
apiEndpoint="https://api.example.com/chat"
onMessageSent={(message) => {
console.log('User sent:', message);
// Send to analytics, etc.
}}
onMessageReceived={(response) => {
console.log('Bot responded:', response);
// Process response
}}
onError={(error) => {
console.error('Chat error:', error);
// Show notification
}}
onOpen={() => console.log('Chat opened')}
onClose={() => console.log('Chat closed')}
/>Multiple Languages with Dynamic Switching
function MultiLingualChat() {
const [locale, setLocale] = useState('en');
return (
<>
<select onChange={(e) => setLocale(e.target.value)}>
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
</select>
<ChatWidget
locale={locale}
apiEndpoint="https://api.example.com/chat"
/>
</>
);
}Integration with Authentication
<ChatWidget
apiEndpoint="https://api.example.com/chat"
customHeaders={{
'Authorization': `Bearer ${userToken}`,
'X-User-ID': userId,
}}
/>Custom Welcome Message with User Data
<ChatWidget
welcomeMessage={`👋 Welcome back, ${userName}! How can I help you today?`}
apiEndpoint="https://api.example.com/chat"
/>TypeScript
Full TypeScript support with comprehensive type definitions:
import {
ChatWidget,
ChatWidgetProps,
ChatMessage,
ChatPlugin,
ChatWidgetTheme,
DisplayMode,
FloatingPosition
} from 'miia-chat-widget';
const config: ChatWidgetProps = {
mode: 'floating',
apiEndpoint: 'https://api.example.com/chat',
theme: {
primaryColor: '#000000',
},
};
<ChatWidget {...config} />Development
To run the development environment:
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build
# Type check
npm run type-check
# Lint
npm run lintBrowser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome Android)
CSS Isolation & Style Safety
The widget uses scoped CSS to ensure it doesn't conflict with your application's styles:
- ✅ Tailwind CSS reset/base styles are scoped to
.miia-chat-widgetcontainer - ✅ Safe to use even if your app uses Tailwind CSS
- ✅ No style conflicts with host application
- ✅ All widget styles are self-contained
The widget automatically wraps itself in a .miia-chat-widget container, ensuring complete style isolation.
Troubleshooting
Widget not appearing
Make sure to import the CSS:
import 'miia-chat-widget/dist/style.css';TypeScript errors
Ensure you have @types/react and @types/react-dom installed:
npm install -D @types/react @types/react-domStyles not applying
The widget includes all necessary styles in its CSS bundle. Styles are automatically scoped to the .miia-chat-widget container to prevent conflicts with your application's styles.
CSS Isolation: The widget uses scoped Tailwind CSS preflight, meaning its reset/base styles only apply within the widget container and won't affect your host application. This ensures safe integration even if your app uses Tailwind CSS.
API not connecting
- Check CORS settings on your API
- Verify
apiEndpointURL is correct - Check browser console for error messages
- Use
demoMode={true}to test without API
History not persisting
- Check if localStorage is available in your browser
- Each widget instance automatically gets a unique
historyKeyif not explicitly provided - If you have multiple widgets and want them to share history, provide the same
historyKeyexplicitly - Check browser privacy settings (some browsers block localStorage in private mode)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT © [Your Name]
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Documentation: Full Docs
Acknowledgments
Built with:
- React
- TypeScript
- Tailwind CSS
- shadcn/ui
- Radix UI
- React Markdown
Made with ❤️ by [Your Name]
