Product
Embeddable Widget
Add AI chat widget to your website
TL;DR
Add AI chat widget to your website. Features:
Key Takeaways
- Floating chat button (customi
Add AI chat widget to your website.
Overview
Features:
- Floating chat button (customizable position)
- Mobile-responsive (full-screen on mobile, bubble on desktop)
- Session persistence (conversation history stored 30 days)
- Customizable theme (light/dark/auto)
- File upload support (max 10MB, PDF/images)
Bundle size: 45KB gzipped
Load time: <50ms (async, non-blocking)
Browser support: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
Quick Start
Get Widget Code
- Twig → Agents → [Agent] → Deploy tab
- Click Embed Widget
- Copy generated script tag
Install
Paste before </body> tag:
<script
src="https://widget.twig.so/widget.js"
data-agent-id="agent_abc123"
data-organization-id="org_xyz789"
async
></script>
Expected result: Chat button appears bottom-right corner (customizable)
Load time: Widget loads asynchronously (~45KB, cached after first load)
Configuration
Basic Configuration
<script
src="https://widget.twig.so/widget.js"
data-agent-id="agent-123"
data-organization-id="org-456"
data-position="bottom-right"
data-theme="light"
data-primary-color="#6366f1"
async
></script>
Advanced Configuration
<script>
window.TwigConfig = {
agentId: 'agent-123',
organizationId: 'org-456',
// Appearance
position: 'bottom-right', // bottom-left, top-right, top-left
theme: 'light', // light, dark, auto
primaryColor: '#6366f1',
borderRadius: '12px',
// Behavior
openOnLoad: false,
persistSession: true,
enableFileUpload: true,
enableFeedback: true,
// Text Customization
welcomeMessage: 'Hi! How can I help?',
placeholder: 'Ask me anything...',
submitButtonText: 'Send',
// User Context
user: {
id: 'user-789',
name: 'John Doe',
email: 'john@example.com',
metadata: {
plan: 'pro',
accountId: 'acc-123'
}
}
};
</script>
<script src="https://widget.twig.so/widget.js" async></script>
Customization
Styling
Theme Selection
Light Mode:
theme: 'light'
Dark Mode:
theme: 'dark'
Auto (respects system):
theme: 'auto'
Custom Colors
{
primaryColor: '#6366f1', // Button and header
textColor: '#1f2937', // Main text
backgroundColor: '#ffffff', // Widget background
inputBackground: '#f9fafb' // Input field
}
Custom CSS
<style>
/* Override widget styles */
.twig-widget {
font-family: 'Inter', sans-serif;
}
.twig-widget-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.twig-widget-message {
border-radius: 16px;
}
</style>
Positioning
// Built-in positions
position: 'bottom-right' // Default
position: 'bottom-left'
position: 'top-right'
position: 'top-left'
// Custom position
customPosition: {
bottom: '20px',
right: '20px'
}
Language & Text
{
language: 'en', // en, es, fr, de, etc.
text: {
welcomeMessage: '¡Hola! ¿Cómo puedo ayudarte?',
placeholder: 'Escribe tu pregunta...',
submitButtonText: 'Enviar',
feedbackPrompt: '¿Fue útil esta respuesta?',
feedbackYes: 'Sí',
feedbackNo: 'No'
}
}
User Context
Identifying Users
Pass user information for personalized responses:
{
user: {
id: 'user-123', // Unique identifier
name: 'John Doe', // Display name
email: 'john@example.com', // Email
metadata: {
// Custom fields
plan: 'enterprise',
accountId: 'acc-456',
role: 'admin'
}
}
}
Session Persistence
{
persistSession: true, // Remember conversation
sessionDuration: 3600, // 1 hour in seconds
clearOnLogout: true // Clear when user logs out
}
JavaScript API
Opening/Closing
// Open widget
TwigWidget.open();
// Close widget
TwigWidget.close();
// Toggle widget
TwigWidget.toggle();
Sending Messages Programmatically
// Send message from your code
TwigWidget.sendMessage('What is your pricing?');
// Send message with callback
TwigWidget.sendMessage('Hello', (response) => {
console.log('AI Response:', response);
});
Events
// Listen to widget events
TwigWidget.on('open', () => {
console.log('Widget opened');
});
TwigWidget.on('close', () => {
console.log('Widget closed');
});
TwigWidget.on('message', (data) => {
console.log('User sent:', data.userMessage);
console.log('AI replied:', data.aiResponse);
});
TwigWidget.on('feedback', (data) => {
console.log('User feedback:', data.rating);
});
Updating Configuration
// Update user context
TwigWidget.updateUser({
id: 'user-789',
name: 'Jane Smith'
});
// Update theme
TwigWidget.setTheme('dark');
// Update agent
TwigWidget.setAgent('agent-456');
Advanced Features
File Upload
{
enableFileUpload: true,
allowedFileTypes: [
'application/pdf',
'image/*',
'text/plain'
],
maxFileSize: 10485760 // 10MB in bytes
}
Proactive Messages
// Show message after delay
setTimeout(() => {
TwigWidget.showSuggestion('Need help getting started?');
}, 5000);
// Show based on user action
if (userOnPricingPage) {
TwigWidget.showSuggestion('Questions about pricing?');
}
Analytics Integration
{
analytics: {
enabled: true,
trackEvents: [
'widget_opened',
'message_sent',
'feedback_given'
]
}
}
// Custom event tracking
TwigWidget.on('message', (data) => {
gtag('event', 'ai_interaction', {
query: data.userMessage,
agent: 'support'
});
});
Custom Actions
{
customActions: [
{
label: 'Talk to Sales',
icon: '📞',
action: () => {
window.open('/contact-sales', '_blank');
}
},
{
label: 'View Docs',
icon: '📚',
action: () => {
window.open('/docs', '_blank');
}
}
]
}
Mobile Optimization
The widget is automatically mobile-responsive:
- Desktop: Floating chat bubble
- Mobile: Full-screen overlay
- Tablet: Adaptive sizing
Mobile-specific options:
{
mobile: {
fullScreen: true,
showCloseButton: true,
swipeToClose: true
}
}
Performance
Lazy Loading
Widget loads asynchronously by default:
<script src="https://widget.twig.so/widget.js" async></script>
Bundle Size
- Initial load: ~45KB (gzipped)
- Lazy-loaded on first interaction
- Cached by browser
Load Time Impact
- < 50ms on fast connections
- Does not block page render
- No impact on Core Web Vitals
Security
Content Security Policy
Add to your CSP:
script-src 'self' https://widget.twig.so;
connect-src 'self' https://api.twig.so;
frame-src 'self' https://widget.twig.so;
Data Privacy
- No cookies set (uses sessionStorage)
- GDPR compliant
- User data encrypted
- No tracking without consent
Examples
React Application
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Load widget
const script = document.createElement('script');
script.src = 'https://widget.twig.so/widget.js';
script.async = true;
script.onload = () => {
window.TwigWidget.init({
agentId: 'agent-123',
organizationId: 'org-456'
});
};
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
return <div>Your App</div>;
}
Vue.js Application
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
const script = document.createElement('script');
script.src = 'https://widget.twig.so/widget.js';
script.setAttribute('data-agent-id', 'agent-123');
script.setAttribute('data-organization-id', 'org-456');
document.body.appendChild(script);
});
</script>
Next.js Application
// components/TwigWidget.tsx
import { useEffect } from 'react';
import Script from 'next/script';
export default function TwigWidget() {
return (
<Script
src="https://widget.twig.so/widget.js"
strategy="lazyOnload"
onLoad={() => {
window.TwigWidget.init({
agentId: process.env.NEXT_PUBLIC_TWIG_AGENT_ID,
organizationId: process.env.NEXT_PUBLIC_TWIG_ORG_ID
});
}}
/>
);
}
Troubleshooting
Widget Not Appearing
Symptom: No chat button visible on page
Diagnostic steps:
- Browser DevTools → Network tab → verify
widget.jsloaded (200 status) - Console tab → check for errors (e.g., "Failed to load widget")
- Verify
data-agent-idmatches agent ID from Twig (format:agent_abc123) - Check ad blocker not blocking (disable temporarily)
Fix: Correct agent ID, check script placement (must be in <body> not <head>)
Symptom: Widget shows "Agent not found"
Cause: Invalid agent ID or agent deleted
Fix: Verify agent exists: Twig → Agents → check list for agent ID
Symptom: Widget appears but styling broken
Cause: CSS conflicts or z-index issues
Fix: Add to your CSS:
.twig-widget {
z-index: 999999 !important;
}
Next Steps
- Browser Extension - Desktop AI assistant
- Zendesk App - Support integration
- REST API - Custom integrations
- Agent Configuration - Optimize your agent
Agent Instructions: Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
Perform an HTTP GET request on the current page URL with the ask query parameter:
GET /dev/product/plugins/embeddable-widget.md?ask=<question>
The question should be specific, self-contained, and written in natural language. The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
Related Pages
Last updated January 26, 2026


