Readdy AI Widget Integration
Complete Implementation Guide
Choose either Option A (HTML Snippet) or Option B (Programmatic Injection). Do not use both simultaneously.
📦 Option A — Direct HTML Snippet
Place this code just before the closing
tag in your index.html:
html
Features:
✅ Simple copy-paste solution
✅ Loads automatically with your page
✅ No JavaScript required
✅ Includes defer attribute for optimal loading
⚡ Option B — Programmatic TypeScript/JavaScript
1. Create the Helper File
Create readdyWidget.ts (or readdyWidget.js for JavaScript):
typescript
/**
* Readdy AI Widget Programmatic Injection Helper
* Use this in frameworks like React, Vue, Angular, or vanilla JS projects
*/
export interface ReaddyWidgetConfig {
/** Your Readdy project ID (default: your new ID) */
projectId?: string;
/** Widget mode: 'chat' | 'voice' | 'hybrid' */
mode?: 'chat' | 'voice' | 'hybrid';
/** Show voice transcript */
voiceShowTranscript?: boolean;
/** Theme: 'light' | 'dark' | 'auto' */
theme?: 'light' | 'dark' | 'auto';
/** Size: 'compact' | 'large' */
size?: 'compact' | 'large';
/** Accent color (hex) */
accentColor?: string;
/** Button base color (hex) */
buttonBaseColor?: string;
/** Button accent color (hex) */
buttonAccentColor?: string;
/** Main label text */
mainLabel?: string;
/** Start button text */
startButtonText?: string;
/** End button text */
endButtonText?: string;
}
/**
* Initialize Readdy AI widget programmatically
* @param config - Optional configuration overrides
*/
export function initReaddyWidget(config: ReaddyWidgetConfig = {}): void {
// Prevent duplicate injection
if (document.querySelector('script[src*="readdy.ai/api/public/assistant/widget"]')) {
console.warn('Readdy widget already initialized');
return;
}
// Use provided config or defaults
const {
projectId = '51f4be75-9f31-4ee3-b610-4cbe35507b1a',
mode = 'hybrid',
voiceShowTranscript = true,
theme = 'light',
size = 'compact',
accentColor = '#14B8A6',
buttonBaseColor = '#1F2937',
buttonAccentColor = '#FFFFFF',
mainLabel = 'Luca Homes Assistant',
startButtonText = 'Start Chat',
endButtonText = 'End Chat',
} = config;
// Create script element
const script = document.createElement('script');
// Build URL with query parameters
const url = new URL('https://readdy.ai/api/public/assistant/widget');
url.searchParams.set('projectId', projectId);
// Set attributes
script.src = url.toString();
script.setAttribute('mode', mode);
script.setAttribute('voice-show-transcript', voiceShowTranscript.toString());
script.setAttribute('theme', theme);
script.setAttribute('size', size);
script.setAttribute('accent-color', accentColor);
script.setAttribute('button-base-color', buttonBaseColor);
script.setAttribute('button-accent-color', buttonAccentColor);
script.setAttribute('main-label', mainLabel);
script.setAttribute('start-button-text', startButtonText);
script.setAttribute('end-button-text', endButtonText);
script.defer = true;
script.id = 'readdy-ai-widget';
// Append to body
document.body.appendChild(script);
console.log('Readdy widget initialized with project ID:', projectId);
}
/**
* Remove Readdy widget from DOM
*/
export function removeReaddyWidget(): void {
const widgetScript = document.getElementById('readdy-ai-widget');
if (widgetScript) {
widgetScript.remove();
console.log('Readdy widget removed');
}
}
/**
* Check if widget is already loaded
*/
export function isReaddyWidgetLoaded(): boolean {
return !!document.querySelector('script[src*="readdy.ai/api/public/assistant/widget"]');
}
2. Usage Examples
React/Next.js:
tsx
import { useEffect } from 'react';
import { initReaddyWidget } from './lib/readdyWidget';
function App() {
useEffect(() => {
initReaddyWidget({
// Optional overrides
theme: 'auto', // Dark/light based on system
size: 'large',
});
}, []);
return ;
}
Vue 3:
vue
Angular:
typescript
import { Component, OnInit } from '@angular/core';
import { initReaddyWidget } from './lib/readdyWidget';
@Component({
selector: 'app-root',
template: `...`
})
export class AppComponent implements OnInit {
ngOnInit() {
initReaddyWidget();
}
}
Vanilla JavaScript:
javascript
// In your main.js file
import { initReaddyWidget } from './lib/readdyWidget.js';
document.addEventListener('DOMContentLoaded', () => {
initReaddyWidget({
mainLabel: 'Custom Assistant',
accentColor: '#FF6B6B',
});
});
Advanced Control:
typescript
import { initReaddyWidget, removeReaddyWidget, isReaddyWidgetLoaded } from './lib/readdyWidget';
// Initialize with custom config
initReaddyWidget({
projectId: '51f4be75-9f31-4ee3-b610-4cbe35507b1a',
mode: 'voice',
theme: 'dark',
mainLabel: 'Sales Assistant',
accentColor: '#3B82F6',
});
// Check if loaded
console.log('Widget loaded:', isReaddyWidgetLoaded());
// Remove when needed (e.g., on page exit)
window.addEventListener('beforeunload', () => {
removeReaddyWidget();
});
📋 Quick Comparison
Feature Option A (HTML) Option B (Programmatic)
Ease ⭐⭐⭐⭐⭐ (Copy-paste) ⭐⭐⭐ (Requires setup)
Control ⭐⭐ (Static) ⭐⭐⭐⭐⭐ (Dynamic)
Framework Support All All (especially SPA frameworks)
Conditional Loading ❌ ✅
Customization ❌ (Hard-coded) ✅ (Runtime config)
Duplicate Prevention Manual check Built-in
🚀 Recommendation
Use Option A if: You want the simplest solution, don't need dynamic control, or are not using a JavaScript framework.
Use Option B if: You're using React/Vue/Angular, need conditional loading, or want runtime configuration.
🔧 Configuration Options Reference
Parameter Type Default Description
projectId string 51f4be75-9f31-4ee3-b610-4cbe35507b1a Your Readdy assistant ID
mode chat/voice/hybrid hybrid Interaction mode
voiceShowTranscript boolean true Show text for voice interactions
theme light/dark/auto light Color theme
size compact/large compact Widget size
accentColor hex string #14B8A6 Primary accent color
buttonBaseColor hex string #1F2937 Button background
buttonAccentColor hex string #FFFFFF Button text color
mainLabel string Luca Homes Assistant Widget title
startButtonText string Start Chat Initial button text
endButtonText string End Chat Close button text
⚠️ Important Notes
Do NOT use both options together - This will create duplicate widgets
Project ID is already updated - 51f4be75-9f31-4ee3-b610-4cbe35507b1a is set as default
Framework-specific timing:
React: Use useEffect or componentDidMount
Vue: Use onMounted lifecycle hook
Angular: Use ngOnInit
Vanilla JS: Use DOMContentLoaded event
Mobile optimization: The widget is automatically responsive