Package Exports
- bettercx-widget
- bettercx-widget/bettercx-widget
- bettercx-widget/loader
- bettercx-widget/react
Readme
BetterCX Widget
Production-ready, embeddable chat widget built with Stencil.js and TypeScript
BetterCX Widget is a lightweight, secure, and highly customizable chat widget that integrates seamlessly with the BetterCX backend. It's designed to be embedded in any website with minimal setup and maximum flexibility.
Features
- 🚀 Lightweight & Fast - Optimized bundles with lazy loading
- 🔒 Secure - JWT-based authentication with origin validation
- 🎨 Customizable - CSS custom properties and runtime theming
- 📱 Responsive - Mobile-first design with touch-friendly controls
- ♿ Accessible - ARIA support and keyboard navigation
- 🌐 Universal - Works with any framework or vanilla HTML
- 🔧 TypeScript - Full type safety and IntelliSense support
- 📊 Observable - Custom events for analytics and monitoring
Quick Start
React Integration (Recommended)
npm install bettercx-widget
import React from 'react';
import { BetterCXWidgetReact } from 'bettercx-widget/react';
function App() {
return (
<div>
<h1>My App</h1>
<BetterCXWidgetReact publicKey="pk_your_widget_key_here" theme="auto" autoInit={true} />
</div>
);
}
Web Component
npm install bettercx-widget
import 'bettercx-widget';
// Use the web component
<bettercx-widget public-key="pk_your_widget_key_here" org-id="123" theme="auto" />;
Script Tag
<script type="module" src="https://unpkg.com/bettercx-widget@latest/dist/bettercx-widget/bettercx-widget.esm.js"></script>
<bettercx-widget public-key="pk_your_widget_key_here" org-id="123" theme="auto"></bettercx-widget>
React Integration
Basic Usage
import React from 'react';
import { BetterCXWidgetReact } from 'bettercx-widget/react';
function App() {
return (
<BetterCXWidgetReact
publicKey="pk_your_widget_key_here"
theme="auto"
onWidgetEvent={event => {
console.log('Widget event:', event);
}}
/>
);
}
Advanced Usage with Controls
import React, { useRef } from 'react';
import { BetterCXWidgetReact, BetterCXWidgetReactRef } from 'bettercx-widget/react';
function App() {
const widgetRef = useRef<BetterCXWidgetReactRef>(null);
const handleOpenChat = () => {
widgetRef.current?.open();
};
const handleSendMessage = () => {
widgetRef.current?.sendMessage('Hello from React!');
};
return (
<div>
<button onClick={handleOpenChat}>Open Chat</button>
<button onClick={handleSendMessage}>Send Message</button>
<BetterCXWidgetReact ref={widgetRef} publicKey="pk_your_widget_key_here" theme="auto" onWidgetEvent={event => console.log('Widget event:', event)} />
</div>
);
}
Props
Prop | Type | Default | Required | Description |
---|---|---|---|---|
publicKey |
string |
- | ✅ | Your widget public key |
theme |
'light' | 'dark' | 'auto' |
'auto' |
❌ | Widget theme |
debug |
boolean |
false |
❌ | Enable debug logging |
baseUrl |
string |
'http://localhost:8000' |
❌ | DB service URL |
aiServiceUrl |
string |
'http://localhost:8081' |
❌ | AI service URL |
autoInit |
boolean |
true |
❌ | Auto-initialize on load |
onWidgetEvent |
function |
- | ❌ | Event handler |
className |
string |
- | ❌ | CSS class name |
style |
object |
- | ❌ | Inline styles |
Methods
const widgetRef = useRef<BetterCXWidgetReactRef>(null);
// Open the chat widget
await widgetRef.current?.open();
// Close the chat widget
await widgetRef.current?.close();
// Toggle the chat widget
await widgetRef.current?.toggle();
// Send a message programmatically
await widgetRef.current?.sendMessage('Hello!');
Configuration
Required Properties
Property | Type | Description |
---|---|---|
publicKey |
string |
Your widget public key (starts with pk_ ) |
Optional Properties
Property | Type | Default | Description |
---|---|---|---|
orgId |
number |
- | Organization ID for fetching configuration |
theme |
'light' | 'dark' | 'auto' |
'auto' |
Theme preference |
palette |
string |
- | Custom color palette name |
disableShadow |
boolean |
false |
Disable Shadow DOM for CSS override |
debug |
boolean |
false |
Enable debug logging |
baseUrl |
string |
- | Backend service URL |
aiServiceUrl |
string |
- | AI service URL |
Events
The widget emits custom events for monitoring and analytics:
const widget = document.querySelector('bettercx-widget');
widget.addEventListener('widgetEvent', event => {
const { type, data, timestamp } = event.detail;
switch (type) {
case 'opened':
// Widget was opened
break;
case 'closed':
// Widget was closed
break;
case 'message-sent':
// User sent a message
break;
case 'message-received':
// AI responded
break;
case 'error':
// An error occurred
break;
case 'config-loaded':
// Configuration was loaded
break;
case 'session-created':
// Authentication session created
break;
}
});
Theming & Customization
CSS Custom Properties
The widget uses CSS custom properties for theming. You can override these in your stylesheet:
bettercx-widget {
--bcx-primary: #007bff;
--bcx-secondary: #6c757d;
--bcx-background: #ffffff;
--bcx-text: #212529;
--bcx-border: #dee2e6;
--bcx-shadow: rgba(0, 0, 0, 0.1);
}
Runtime Configuration
The widget automatically fetches configuration from your backend when orgId
is provided. This includes:
- Color schemes (light/dark mode)
- Branding elements
- Feature toggles
- Custom styling
Platform-Specific Integration
WordPress
Create a shortcode in your theme's functions.php
:
function bettercx_widget_shortcode($atts) {
$atts = shortcode_atts(array(
'public_key' => '',
'org_id' => '',
'theme' => 'auto'
), $atts);
if (empty($atts['public_key'])) {
return '<!-- BetterCX Widget: public_key required -->';
}
return sprintf(
'<script type="module" src="https://unpkg.com/bettercx-widget@latest/dist/bettercx-widget/bettercx-widget.esm.js"></script>
<bettercx-widget
public-key="%s"
org-id="%s"
theme="%s">
</bettercx-widget>',
esc_attr($atts['public_key']),
esc_attr($atts['org_id']),
esc_attr($atts['theme'])
);
}
add_shortcode('bettercx_widget', 'bettercx_widget_shortcode');
Usage: [bettercx_widget public_key="pk_your_key" org_id="123"]
Shopify
Add to your theme's theme.liquid
before </body>
:
<script type="module" src="https://unpkg.com/bettercx-widget@latest/dist/bettercx-widget/bettercx-widget.esm.js"></script>
<bettercx-widget
public-key="{{ settings.bettercx_public_key }}"
org-id="{{ settings.bettercx_org_id }}"
theme="{{ settings.bettercx_theme | default: 'auto' }}">
</bettercx-widget>
Webflow
Add this custom embed code to your page:
<script type="module" src="https://unpkg.com/bettercx-widget@latest/dist/bettercx-widget/bettercx-widget.esm.js"></script>
<bettercx-widget public-key="pk_your_widget_key_here" org-id="123" theme="auto"> </bettercx-widget>
Development
Prerequisites
- Node.js 18+
- npm or yarn
Setup
git clone https://github.com/BetterCXAppWave/widget.git
cd widget
npm install
Development Server
npm start
Build
npm run build
Testing
# Unit tests
npm run test:unit
# E2E tests
npm run test:e2e
# All tests
npm test
Linting
npm run lint
npm run lint:fix
API Reference
BetterCXWidget Class
class BetterCXWidget {
constructor(options: WidgetOptions);
open(): Promise<void>;
close(): Promise<void>;
toggle(): Promise<void>;
sendMessage(content: string): Promise<void>;
on(event: string, callback: Function): void;
off(event: string, callback: Function): void;
destroy(): void;
}
WidgetOptions Interface
interface WidgetOptions {
publicKey: string;
orgId?: number;
theme?: 'light' | 'dark' | 'auto';
palette?: string;
disableShadow?: boolean;
debug?: boolean;
baseUrl?: string;
aiServiceUrl?: string;
container?: HTMLElement;
}
Security
- Origin Validation: Widget keys are validated against allowed origins
- JWT Tokens: Short-lived tokens (1 hour) for API authentication
- No Local Storage: Tokens are stored in memory only
- CORS Protection: Backend validates all cross-origin requests
- Rate Limiting: Built-in protection against abuse
Browser Support
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.