Skip to content

A production-ready, embeddable AI chatbot widget built with Cloudflare Workers that can be integrated into any website with a single script tag. Features real-time streaming responses, RAG-powered FAQ answers, session persistence, and dark/light mode support.

License

Notifications You must be signed in to change notification settings

arnobt78/Embeddable-AI-Chatbot-Widget--JavaScript-Cloudflare-Workers-FullStack

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

12 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Embeddable RAG AI Chatbot Widget - JavaScript, Cloudflare Workers FullStack Project

A production-ready, embeddable AI chatbot widget built with Cloudflare Workers that can be integrated into any website with a single script tag. Features real-time streaming responses, RAG-powered FAQ answers, session persistence, and dark/light mode support.


πŸ“‹ Table of Contents


✨ Features

  • πŸš€ Real-time Streaming: AI responses stream in real-time for a natural typing effect
  • 🧠 RAG (Retrieval Augmented Generation): Answers questions from your FAQ using semantic search
  • πŸ’Ύ Session Persistence: Remembers conversations across page reloads using Cloudflare KV
  • πŸŒ“ Dark/Light Mode: Automatic theme detection with manual toggle option
  • πŸ“± Fully Responsive: Works seamlessly on desktop, tablet, and mobile devices
  • πŸ”’ Shadow DOM: Complete CSS isolation prevents conflicts with host website styles
  • ⚑ Edge Computing: Runs on Cloudflare's global edge network for low latency
  • 🎨 Customizable: Easy to customize appearance, behavior, and AI personality
  • πŸ†“ Free Tier: Generous free tiers from Cloudflare for most use cases

πŸ›  Technology Stack

Backend

  • Cloudflare Workers: Serverless edge computing platform
  • Workers AI: Meta's Llama 3-8B model for AI responses
  • Vectorize: Vector database for semantic FAQ search
  • KV (Key-Value): Distributed storage for session management

Frontend

  • Vanilla JavaScript: No framework dependencies, pure JS
  • Shadow DOM: Web Components API for style isolation
  • Tailwind CSS: Utility-first CSS framework
  • Server-Sent Events (SSE): Real-time streaming protocol

Development Tools

  • Wrangler: Cloudflare CLI for development and deployment
  • Tailwind CSS: CSS framework with JIT compilation
  • Node.js: JavaScript runtime environment

πŸ“ Project Structure

ai-chatbot-widget/
β”œβ”€β”€ public/                 # Static assets served by Worker
β”‚   β”œβ”€β”€ widget.js          # Embeddable widget script (frontend)
β”‚   β”œβ”€β”€ styles.css         # Compiled Tailwind CSS
β”‚   β”œβ”€β”€ index.html         # Demo page with SEO metadata
β”‚   └── logo.svg           # Widget favicon/logo
β”œβ”€β”€ src/                   # Source files
β”‚   β”œβ”€β”€ index.js           # Cloudflare Worker backend
β”‚   └── input.css          # Tailwind source CSS
β”œβ”€β”€ wrangler.jsonc         # Cloudflare Workers configuration
β”œβ”€β”€ tailwind.config.js     # Tailwind CSS configuration
β”œβ”€β”€ package.json           # Node.js dependencies and scripts
└── README.md              # Project documentation

File Descriptions

  • src/index.js: Main Worker file handling API routes, RAG, streaming, and session management
  • public/widget.js: Self-contained embeddable widget with Shadow DOM implementation
  • public/index.html: Demo page showcasing the widget with comprehensive SEO metadata
  • wrangler.jsonc: Configuration for Cloudflare services (AI, Vectorize, KV, Assets)
  • tailwind.config.js: Tailwind configuration for CSS compilation

πŸ“¦ Prerequisites

Before you begin, ensure you have:

  • Node.js version 18 or higher installed
  • Cloudflare account (free tier works perfectly)
  • Basic knowledge of JavaScript
  • Git (optional, for version control)

No prior experience with Cloudflare Workers is required!


πŸš€ Installation & Setup

Step 1: Clone or Create Project

If starting fresh:

npm create cloudflare@latest ai-chatbot-widget -- --type=hello-world
cd ai-chatbot-widget

Step 2: Install Dependencies

npm install --save-dev tailwindcss autoprefixer postcss wrangler

Step 3: Login to Cloudflare

npx wrangler login

This opens a browser window for authentication.

Step 4: Create Cloudflare Resources

Create Vectorize Index (for RAG)

npx wrangler vectorize create faq-vectors --dimensions=768 --metric=cosine

What this does: Creates a vector database with 768-dimensional vectors (BGE embedding size) using cosine similarity for semantic search.

Create KV Namespace (for Sessions)

npx wrangler kv namespace create CHAT_SESSIONS

Important: Copy the id from the output. You'll need it for wrangler.jsonc.

Step 5: Configure Wrangler

Update wrangler.jsonc with your KV namespace ID:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "ai-chatbot-widget",
  "main": "src/index.js",
  "compatibility_date": "2025-12-23",
  "observability": {
    "enabled": true,
  },
  "assets": {
    "directory": "./public",
    "binding": "ASSETS",
  },
  "ai": {
    "binding": "AI",
  },
  "vectorize": [
    {
      "binding": "VECTORIZE",
      "index_name": "faq-vectors",
    },
  ],
  "kv_namespaces": [
    {
      "binding": "CHAT_SESSIONS",
      "id": "YOUR_KV_NAMESPACE_ID", // Replace with your ID
    },
  ],
}

Step 6: Build CSS

npm run build:css

This compiles Tailwind CSS into public/styles.css.


βš™οΈ Configuration

Wrangler Configuration (wrangler.jsonc)

This file configures Cloudflare Workers bindings:

  • assets: Serves static files from ./public directory
  • ai: Enables Workers AI for LLM and embeddings
  • vectorize: Links to Vectorize index for RAG
  • kv_namespaces: Connects to KV for session storage

Widget Configuration (JavaScript)

Configure the widget before loading:

<script>
  window.CHATBOT_BASE_URL = "https://your-worker.workers.dev";
  window.CHATBOT_TITLE = "Support Assistant";
  window.CHATBOT_PLACEHOLDER = "Type your message...";
  window.CHATBOT_GREETING = "πŸ‘‹ Hi! How can I help you today?";
</script>
<script src="https://your-worker.workers.dev/widget.js"></script>

Environment Variables

Note: Cloudflare Workers don't use traditional .env files. Configuration is done through:

  1. wrangler.jsonc: Service bindings (AI, Vectorize, KV)
  2. Wrangler secrets (if needed): wrangler secret put SECRET_NAME
  3. Widget globals: Set via window.CHATBOT_* variables

πŸ”§ How It Works

Architecture Overview

User's Browser
    β”‚
    β”œβ”€> Loads widget.js
    β”‚   └─> Creates Shadow DOM
    β”‚   └─> Injects chat UI
    β”‚
    └─> User sends message
        β”‚
        └─> POST /api/chat
            β”‚
            β”œβ”€> Extract session from cookie
            β”œβ”€> Generate question embedding (BGE model)
            β”œβ”€> Search Vectorize for similar FAQs (RAG)
            β”œβ”€> Build AI prompt with FAQ context
            β”œβ”€> Stream response from Llama 3
            └─> Save session to KV

Flow Breakdown

  1. Widget Initialization:

    • Widget script loads and creates Shadow DOM host
    • Injects HTML structure and styles
    • Loads chat history from /api/history
    • Applies theme based on system preference
  2. Message Sending:

    • User types message and submits
    • Widget sends POST to /api/chat with credentials
    • Backend extracts session ID from cookie
  3. RAG Processing:

    • Question converted to 768-dim vector using BGE model
    • Vectorize searches for top 3 similar FAQ entries
    • FAQ context formatted and added to AI prompt
  4. AI Response:

    • System prompt + FAQ context + conversation history sent to Llama 3
    • Response streams back as Server-Sent Events
    • Widget updates UI in real-time as tokens arrive
  5. Session Persistence:

    • Complete conversation saved to KV with 30-day TTL
    • Session ID stored in HTTP-only cookie
    • History restored on next visit

πŸ”Œ API Endpoints

POST /api/chat

Handles chat messages and streams AI responses.

Request:

{
  "message": "What is your return policy?"
}

Response: Server-Sent Events stream

data: {"response": "Our return policy"}
data: {"response": " allows"}
...
data: [DONE]

Features:

  • Session management via cookies
  • RAG-powered FAQ context
  • Streaming responses
  • Automatic session creation

GET /api/history

Retrieves conversation history for current session.

Response:

{
  "messages": [
    {
      "role": "user",
      "content": "Hello",
      "timestamp": 1234567890
    },
    {
      "role": "assistant",
      "content": "Hi! How can I help?",
      "timestamp": 1234567891
    }
  ]
}

Uses: Restores chat history on widget load


POST /api/seed

Populates Vectorize index with FAQ embeddings.

Request: None (uses hardcoded FAQs in src/index.js)

Response:

{
  "success": true,
  "count": 8
}

Usage: Call once after deployment to populate knowledge base

curl -X POST https://your-worker.workers.dev/api/seed

GET /api/health

Health check endpoint.

Response:

{
  "status": "ok"
}

Static Assets

All files in ./public are served automatically:

  • /widget.js - Embeddable widget script
  • /styles.css - Compiled Tailwind CSS
  • /index.html - Demo page
  • /logo.svg - Favicon/logo

🧩 Components & Architecture

Backend Components (src/index.js)

1. Session Management

const cookie = (r) =>
  r.headers.get("Cookie")?.match(/chatbot_session=([^;]+)/)?.[1];
  • Extracts session ID from HTTP cookie
  • Creates new session if none exists
  • Stores session in KV with 30-day expiration

2. RAG Function (faq())

async function faq(env, q) {
  // Generate embedding
  const e = await env.AI.run("@cf/baai/bge-base-en-v1.5", { text: [q] });
  // Search Vectorize
  const r = await env.VECTORIZE.query(e.data[0], { topK: 3 });
  // Format results
  return r.matches
    .map((m) => `Q: ${m.metadata?.question}\nA: ${m.metadata?.answer}`)
    .join("\n\n");
}

How it works:

  1. Converts question to vector embedding
  2. Searches Vectorize for semantically similar FAQs
  3. Returns formatted context for AI prompt

3. Chat Handler (chat())

async function chat(req, env) {
  // Get/create session
  // Add user message
  // Get FAQ context (RAG)
  // Build AI prompt
  // Stream response
  // Save to KV
}

Key features:

  • TransformStream for real-time streaming
  • Accumulates full response for KV storage
  • Sets HTTP-only cookie for new sessions

4. Streaming Response

const { readable, writable } = new TransformStream({
  transform(chunk, ctrl) {
    // Parse SSE format
    // Accumulate response
    ctrl.enqueue(chunk); // Forward to client
  },
  async flush() {
    // Save complete response to KV
  },
});

Frontend Components (public/widget.js)

1. Shadow DOM Implementation

const host = document.createElement("div");
host.attachShadow({ mode: "open" });
shadowRoot = host.shadowRoot;

Why Shadow DOM?

  • CSS Isolation: Prevents Tailwind from affecting host page
  • Encapsulation: Widget styles don't leak out
  • No Conflicts: Host page styles don't affect widget

2. State Management

let open = 0; // Chat window open/closed
let msgs = []; // Message array
let typing = 0; // Request in progress
let menu = 0; // Menu open/closed
let dark = false; // Theme mode

3. Message Rendering (draw())

function draw() {
  $('cb-ms').innerHTML = msgs.map((m, i) =>
    m.role === 'user'
      ? /* User message HTML */
      : /* Assistant message HTML */
  ).join('');
}
  • Maps message array to HTML
  • User messages: Right-aligned, black background
  • Assistant messages: Left-aligned, includes bot icon
  • Auto-scrolls to bottom

4. Streaming Handler (send())

const rd = r.body.getReader();
while (1) {
  const { done, value } = await rd.read();
  // Parse SSE chunks
  // Update message in real-time
}

Process:

  1. Reads stream chunk by chunk
  2. Parses Server-Sent Events format
  3. Updates message element incrementally
  4. Creates typing effect

5. Theme Management

function theme() {
  tog($("cb"), "dark", dark);
  // Updates icons and text
}
  • Toggles dark class on root element
  • Tailwind's dark: variants activate automatically
  • Persists across page reloads

πŸ“– Usage Guide

Local Development

  1. Start development server:
npm run dev

This runs:

  • npm run build:css - Compiles Tailwind
  • wrangler dev - Starts local Worker
  1. Access locally: Open http://localhost:8787 in your browser

  2. Test widget:

  • Chat with the AI
  • Test dark mode toggle
  • Clear chat and verify persistence

Deploying to Production

  1. Build and deploy:
npm run deploy

This:

  • Builds CSS
  • Deploys Worker to Cloudflare
  • Returns deployment URL
  1. Seed FAQ database:
curl -X POST https://your-worker.workers.dev/api/seed
  1. Test deployment: Visit your Worker URL and test the chatbot

Embedding on Any Website

Add these two script tags before </body>:

<script>
  window.CHATBOT_BASE_URL = "https://your-worker.workers.dev";
  window.CHATBOT_TITLE = "Support";
  window.CHATBOT_GREETING = "πŸ‘‹ How can I help you today?";
</script>
<script src="https://your-worker.workers.dev/widget.js"></script>

That's it! The widget will automatically appear in the bottom-right corner.


🎨 Customization

Changing AI Personality

Edit SYS constant in src/index.js:

const SYS = `You are a friendly assistant for [Your Company].
You help customers with [your services].
Always be helpful and professional.`;

Adding Your Own FAQs

Edit the faqs array in seed() function:

const faqs = [
  ["Your question?", "Your answer."],
  ["Another question?", "Another answer."],
  // Add more...
];

Then redeploy and reseed:

npm run deploy
curl -X POST https://your-worker.workers.dev/api/seed

Styling the Widget

All styles use Tailwind classes in widget.js. Common customizations:

Colors:

// Change button color
class="bg-black" β†’ class="bg-blue-600"

Size:

// Chat window dimensions
w-[400px] h-[600px] β†’ w-[500px] h-[700px]

Position:

// Button position
bottom-6 right-6 β†’ bottom-4 left-4

Widget Configuration Options

Variable Description Default
CHATBOT_BASE_URL Worker deployment URL '' (same origin)
CHATBOT_TITLE Header title 'AI Assistant'
CHATBOT_PLACEHOLDER Input placeholder 'Message...'
CHATBOT_GREETING Initial greeting 'πŸ‘‹ Hi! How can I help you today?'

πŸš€ Deployment

Prerequisites

  • Cloudflare account
  • Wrangler CLI installed
  • Resources created (Vectorize, KV)

Deployment Steps

  1. Build CSS:
npm run build:css
  1. Deploy Worker:
wrangler deploy

Or use the combined command:

npm run deploy
  1. Verify Deployment:

Visit your Worker URL (e.g., https://ai-chatbot-widget.YOUR-SUBDOMAIN.workers.dev)

  1. Seed FAQs:
curl -X POST https://your-worker.workers.dev/api/seed

Cloudflare Free Tier Limits

  • Workers: 100,000 requests/day
  • Workers AI: 10,000 neurons/day
  • Vectorize: 5M vector operations/month
  • KV: 100K reads, 1K writes/day

Most websites can run completely free!


πŸ”„ Reusing Components

Using the Widget in Other Projects

The widget is completely self-contained. To reuse:

  1. Copy widget.js to your project
  2. Host styles.css (or inline it)
  3. Set configuration globals:
window.CHATBOT_BASE_URL = "https://your-api.com";

Extracting RAG Function

The faq() function can be reused for any RAG use case:

// In your own Worker
async function searchKnowledgeBase(env, query) {
  const embedding = await env.AI.run("@cf/baai/bge-base-en-v1.5", {
    text: [query],
  });
  const results = await env.VECTORIZE.query(embedding.data[0], {
    topK: 5,
    returnMetadata: "all",
  });
  return results.matches;
}

Reusing Shadow DOM Pattern

The Shadow DOM implementation can be adapted for any embeddable widget:

function createIsolatedWidget() {
  const host = document.createElement("div");
  host.attachShadow({ mode: "open" });
  const shadow = host.shadowRoot;

  // Inject styles and HTML
  shadow.appendChild(style);
  shadow.appendChild(content);

  document.body.appendChild(host);
  return shadow;
}

Streaming Response Pattern

The streaming pattern works for any SSE endpoint:

async function streamResponse(url, onChunk) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const text = decoder.decode(value);
    const lines = text.split("\n");

    for (const line of lines) {
      if (line.startsWith("data: ")) {
        const data = JSON.parse(line.slice(6));
        onChunk(data);
      }
    }
  }
}

🏷 Keywords

Technologies: Cloudflare Workers, Workers AI, Vectorize, KV, RAG, Server-Sent Events, Shadow DOM, Tailwind CSS, Llama 3, BGE embeddings, semantic search

Concepts: Embeddable widget, serverless, edge computing, real-time streaming, session persistence, dark mode, responsive design, CSS isolation, vector database, retrieval augmented generation

Use Cases: Customer support, FAQ chatbot, website assistant, AI assistant, conversational AI, knowledge base search


πŸ“š Educational Content

Understanding RAG (Retrieval Augmented Generation)

RAG enhances AI responses by providing relevant context from a knowledge base:

  1. User asks question β†’ "What is your return policy?"
  2. Question converted to vector β†’ [0.123, -0.456, ...] (768 dimensions)
  3. Vectorize searches β†’ Finds similar FAQ entries
  4. Context added to prompt β†’ "FAQ: Q: What is your return policy? A: 30-day returns..."
  5. AI generates response β†’ Uses FAQ context for accurate answer

Benefits:

  • More accurate than pure AI
  • Can be updated without retraining
  • Grounded in your data
  • Reduces hallucinations

Shadow DOM Deep Dive

Shadow DOM creates an isolated DOM tree:

// Regular DOM
document.body.appendChild(element); // Styles leak

// Shadow DOM
const shadow = host.attachShadow({ mode: "open" });
shadow.appendChild(element); // Styles isolated

Benefits for widgets:

  • No CSS conflicts
  • Encapsulated styles
  • Reusable components
  • Works on any website

Streaming Responses Explained

Instead of waiting for complete response:

// Traditional (slow)
const response = await fetch("/api/chat");
const data = await response.json();
display(data.message); // User waits

// Streaming (fast)
const stream = await fetch("/api/chat");
const reader = stream.body.getReader();
while ((chunk = await reader.read())) {
  display(chunk); // User sees progress
}

Benefits:

  • Perceived performance
  • Natural typing effect
  • Better UX
  • Lower latency

πŸŽ“ Learning Resources

Cloudflare Workers

RAG & Vector Search

Shadow DOM

Server-Sent Events


πŸ› Troubleshooting

Widget Not Appearing

  • Check browser console for errors
  • Verify CHATBOT_BASE_URL is set correctly
  • Ensure script loads after DOM ready

CSS Not Loading

  • Run npm run build:css before deploying
  • Check styles.css exists in public/
  • Verify Worker serves static assets

RAG Not Working

  • Ensure Vectorize index is created
  • Call /api/seed endpoint
  • Check Vectorize bindings in wrangler.jsonc

Session Not Persisting

  • Verify KV namespace ID in wrangler.jsonc
  • Check cookies are enabled
  • Ensure credentials: 'include' in fetch calls


πŸ“ License

This project is open source and available under the MIT License.


🀝 Contributing

Contributions are welcome! Feel free to:

  • Report bugs
  • Suggest features
  • Submit pull requests
  • Improve documentation

πŸ™ Acknowledgments


πŸ“ž Support

For questions or issues:


πŸŽ‰ Happy Coding

Feel free to use this project repository and extend this project further!

If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.

Enjoy building and learning! πŸš€

Thank you! 😊


About

A production-ready, embeddable AI chatbot widget built with Cloudflare Workers that can be integrated into any website with a single script tag. Features real-time streaming responses, RAG-powered FAQ answers, session persistence, and dark/light mode support.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published