Embeddable AI chatbot for documentation, powered by SQLite Cloud.
For local testing, the package includes a built-in mock endpoint: mock://docs-chatbot.
The repo demos and examples now prefer that mock endpoint by default, even if VITE_SEARCH_API_URL exists.
Before using this chatbot, you need to:
- Index your documentation - Use the SQLite AI Search Action to create embeddings from your documentation files
- Create an edge function - Follow the setup guide to deploy the search edge function
npm install @sqliteai/docs-chatbotimport { DocsChatbot } from "@sqliteai/docs-chatbot";
import "@sqliteai/docs-chatbot/style.css";
function App() {
return (
<DocsChatbot
search={{
url: "https://yourproject.sqlite.cloud/v2/functions/aisearch-docs",
apiKey: "your-api-key",
}}
title="Help Center"
variant="embedded"
/>
);
}For local-only testing, replace search.url with mock://docs-chatbot and use any placeholder API key such as demo-key.
If you want the demos to use a real backend instead, set VITE_USE_REAL_SEARCH=true.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<script src="https://unpkg.com/@sqliteai/docs-chatbot/dist/umd/docs-chatbot.min.js"></script>
<docs-chatbot
search-url="https://yourproject.sqlite.cloud/v2/functions/aisearch-docs"
api-key="your-api-key"
title="Help Center"
>
</docs-chatbot>
</body>
</html>Render the chatbot inline inside your layout. This is the mode to use when you want the chat UI to live inside an existing panel, sidebar, or page section.
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Help Center"
variant="embedded"
className="max-w-2xl h-[600px]"
persistence={{ key: "help-center" }}
results={{ snippetMaxLines: 5 }}
/><docs-chatbot
search-url="https://yourproject.sqlite.cloud/v2/functions/aisearch-docs"
api-key="your-api-key"
title="Help Center"
variant="embedded"
></docs-chatbot>In embedded mode, the host layout should provide height. The component no longer renders its own outer frame.
To preserve chat history across unmounts or context switches, pass a persistence key. In dashboard-style layouts, use a different key per database or per workspace.
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Memory Assistant"
variant="embedded"
className="h-full"
persistence={{
key: `memory:${projectId}:${databaseName}`,
storage: "session",
}}
/><docs-chatbot
search-url="https://yourproject.sqlite.cloud/v2/functions/aisearch-docs"
api-key="your-api-key"
title="Help Center"
persistence-key="help-center"
persistence-storage="session"
></docs-chatbot>The floating dialog modes also keep running when you click outside the widget. They no longer dismiss on background clicks.
To keep result cards compact, you can clamp snippet height visually and/or truncate by characters.
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Memory Assistant"
variant="embedded"
results={{
snippetMaxLines: 5,
snippetMaxChars: 320,
}}
/>To intercept result clicks and route them into your own UI, use results.onSelect in React or listen for the resultselect event on the web component.
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Memory Assistant"
variant="embedded"
results={{
onSelect: (result) => {
// Example: select the matching file in your tree/editor
console.log("Selected result", result);
},
}}
/><docs-chatbot
search-url="https://yourproject.sqlite.cloud/v2/functions/aisearch-docs"
api-key="your-api-key"
title="Help Center"
></docs-chatbot>
<script>
const chatbot = document.querySelector("docs-chatbot");
chatbot.addEventListener("resultselect", (event) => {
event.preventDefault();
console.log("Selected result", event.detail);
});
</script>
Adds a floating button in the bottom-right corner that opens the chatbot when clicked.
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Help Center"
variant="dialog"
/>
Use your own button or trigger element to control when the chatbot opens. This mode is useful when you want the chatbot to integrate seamlessly with your existing UI design or place the trigger in a specific location (like a navigation bar or help menu).
React:
import { DocsChatbot } from "@sqliteai/docs-chatbot";
import "@sqliteai/docs-chatbot/style.css";
import { useState } from "react";
function App() {
const [open, setOpen] = useState(false);
return (
<>
{/* Your custom button anywhere in your app */}
<button onClick={() => setOpen(true)}>Help & Support</button>
{/* Chatbot with custom trigger mode */}
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Help Center"
dialog={{ trigger: "custom", open, onOpenChange: setOpen }}
/>
</>
);
}Vanilla JavaScript:
<script src="https://unpkg.com/@sqliteai/docs-chatbot/dist/umd/docs-chatbot.min.js"></script>
<!-- Your custom button -->
<button id="help-btn">Help & Support</button>
<!-- Chatbot with custom trigger mode -->
<docs-chatbot
search-url="your-edge-function-url"
api-key="your-api-key"
title="Help Center"
trigger="custom"
>
</docs-chatbot>
<script>
const chatbot = document.querySelector("docs-chatbot");
const button = document.getElementById("help-btn");
// Open chatbot when button is clicked
button.addEventListener("click", () => {
chatbot.open = true;
});
// Listen to state changes (optional)
chatbot.addEventListener("openchange", (e) => {
console.log("Chatbot open:", e.detail.open);
});
</script>| Property | Type | Required | Description |
|---|---|---|---|
search |
{ url: string; apiKey: string } |
Yes | Search transport configuration |
search.url |
string |
Yes | Full URL of your deployed SQLite Cloud edge function (e.g., https://yourproject.sqlite.cloud/v2/functions/aisearch-docs) |
search.apiKey |
string |
Yes | SQLite Cloud API key with permissions to execute the edge function |
title |
string |
Yes | Title displayed in the chatbot header |
variant |
"dialog" | "embedded" |
No | Rendering mode: "dialog" keeps the popup widget behavior, "embedded" renders the chatbot inline (default: "dialog") |
emptyState |
{ title: string; description: string } |
No | Customizes the initial empty state of the chatbot |
emptyState.title |
string |
No | Main heading shown before the first message |
emptyState.description |
string |
No | Subtext shown below the empty state title |
persistence |
{ key: string; storage?: "session" | "local" } |
No | Persists messages and composer input under the provided key |
persistence.key |
string |
No | Storage key used for persisted conversation state |
persistence.storage |
"session" | "local" |
No | Storage backend for persistence (default: "session") |
header |
{ showClearButton?: boolean } |
No | Header-specific controls |
header.showClearButton |
boolean |
No | Shows the Clear action in the header when there is conversation history (default: false) |
results |
{ onSelect?: (result: DocumentSearchResult) => void; snippetMaxLines?: number; snippetMaxChars?: number } |
No | Result-card behavior and display settings |
results.onSelect |
(result: DocumentSearchResult) => void |
No | Called when a result card is selected. When provided, default link navigation is suppressed |
results.snippetMaxLines |
number |
No | Visually clamps result snippets to the given number of lines |
results.snippetMaxChars |
number |
No | Truncates result snippets to the given number of characters before rendering |
dialog |
{ trigger?: "default" } | { trigger: "custom"; open: boolean; onOpenChange: (open: boolean) => void } |
No | Dialog-specific configuration |
dialog.trigger |
"default" | "custom" |
No | Trigger mode for dialog rendering. "default" uses the floating button, "custom" makes open state controlled |
dialog.open |
boolean |
Yes when dialog.trigger="custom" |
Controls the chatbot open state in custom-trigger mode |
dialog.onOpenChange |
(open: boolean) => void |
Yes when dialog.trigger="custom" |
Callback fired when the open state changes in custom-trigger mode |
className |
string |
No | Extra classes applied to the root chatbot panel |
style |
CSSProperties |
No | Inline styles applied to the root chatbot panel |
| Attribute | Required | Description |
|---|---|---|
search-url |
Yes | Full URL of your deployed SQLite Cloud edge function (e.g., https://yourproject.sqlite.cloud/v2/functions/aisearch-docs) |
api-key |
Yes | SQLite Cloud API key with permissions to execute the edge function |
title |
Yes | Title displayed in the chatbot header |
empty-state-title |
No | Main heading shown before the first message |
empty-state-description |
No | Subtext shown below the empty state title |
persistence-key |
No | Storage key used to persist messages and composer input |
persistence-storage |
No | Storage backend for persistence: "session" or "local" (default: "session") |
result-snippet-max-lines |
No | Visually clamps result snippets to the given number of lines |
result-snippet-max-chars |
No | Truncates result snippets to the given number of characters |
show-clear-button |
No | When present, shows the Clear action in the header |
variant |
No | Rendering mode: "dialog" for the popup widget or "embedded" for an inline panel |
trigger |
No | Trigger mode: "default" uses floating button, "custom" requires controlling open property (default: "default") |
| Property | Type | Description |
|---|---|---|
open |
boolean |
Get or set the chatbot open state (property-only, no attribute) |
| Event | Detail | Description |
|---|---|---|
openchange |
{ open: boolean } |
Fired when the chatbot open state changes |
resultselect |
DocumentSearchResult |
Fired when a result card is selected. Call preventDefault() to suppress default link navigation |
Customize the chatbot's appearance using CSS variables.
| Variable | Description |
|---|---|
--docs-chatbot-radius |
Border radius |
--docs-chatbot-background |
Background color |
--docs-chatbot-foreground |
Text color |
--docs-chatbot-primary |
Primary color |
--docs-chatbot-primary-foreground |
Primary text color |
--docs-chatbot-secondary |
Secondary color |
--docs-chatbot-secondary-foreground |
Secondary text color |
--docs-chatbot-muted |
Muted color |
--docs-chatbot-muted-foreground |
Muted text color |
--docs-chatbot-accent |
Accent color |
--docs-chatbot-accent-foreground |
Accent text color |
--docs-chatbot-border |
Border color |
--docs-chatbot-input |
Input background color |
--docs-chatbot-ring |
Focus ring color |
--docs-chatbot-card |
Card background color |
--docs-chatbot-card-foreground |
Card text color |
--docs-chatbot-popover |
Popover background color |
--docs-chatbot-popover-foreground |
Popover text color |
--docs-chatbot-destructive |
Destructive/error color |
React:
/* In your main CSS file, import the chatbot styles first */
@import "@sqliteai/docs-chatbot/style.css";
/* Then override the variables */
:root {
--docs-chatbot-primary: oklch(0.6 0.2 0);
--docs-chatbot-primary-foreground: oklch(1 0 0);
--docs-chatbot-border: oklch(0.85 0 0);
--docs-chatbot-radius: 8px;
}import { DocsChatbot } from "@sqliteai/docs-chatbot";
import "./styles.css"; // Your CSS file with overrides
function App() {
return (
<DocsChatbot
search={{ url: "your-edge-function-url", apiKey: "your-api-key" }}
title="Help Center"
/>
);
}Vanilla JavaScript:
<style>
docs-chatbot {
--docs-chatbot-primary: oklch(0.6 0.2 0);
--docs-chatbot-primary-foreground: oklch(1 0 0);
--docs-chatbot-border: oklch(0.85 0 0);
--docs-chatbot-radius: 8px;
}
</style>
<docs-chatbot
search-url="your-edge-function-url"
api-key="your-api-key"
title="Help Center"
>
</docs-chatbot>