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

  1. Twig → Agents → [Agent] → Deploy tab
  2. Click Embed Widget
  3. 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:

  1. Browser DevTools → Network tab → verify widget.js loaded (200 status)
  2. Console tab → check for errors (e.g., "Failed to load widget")
  3. Verify data-agent-id matches agent ID from Twig (format: agent_abc123)
  4. 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


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