A vanilla JavaScript chat widget with zero runtime dependencies.
Themeable, LLM-ready, under 5KB gzipped.
This is a live widget. Type a message and hit Send (or Shift+Enter).
npm install quikchat
<link rel="stylesheet" href="https://unpkg.com/quikchat/dist/quikchat.css">
<script src="https://unpkg.com/quikchat/dist/quikchat.umd.min.js"></script>
import quikchat from 'quikchat';
Create a container, include quikchat, and initialize with a callback:
<div id="chat" style="height: 400px;"></div>
<script>
const chat = new quikchat("#chat",
(chat, msg) => {
chat.messageAddNew(msg, "me", "right");
// send msg to your backend / LLM and post the response:
chat.messageAddNew("Got it!", "bot", "left");
},
{
theme: "quikchat-theme-light",
titleArea: { title: "My Chat", align: "left", show: true }
}
);
</script>
The callback fires when the user clicks Send or presses Shift+Enter. Messages are not auto-echoed, so you control what appears and when.
QuikChat tracks full message history. Feed it directly to any OpenAI-compatible API:
const history = chat.historyGet().map(m => ({
role: m.role,
content: m.content
}));
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + API_KEY
},
body: JSON.stringify({ model: "gpt-4o", messages: history })
});
Works with OpenAI, Ollama, LM Studio, Anthropic, Mistral, or any provider with a compatible API. See the Ollama and OpenAI examples for complete working code.
Themes are CSS classes applied to the widget root. Switch themes at runtime:
chat.changeTheme("quikchat-theme-dark");
Seven themes are included: light, blue, warm,
dark, midnight, ocean, and debug
(all prefixed quikchat-theme-).
To create a custom theme, see the Theming Guide.
new quikchat(parentElement, onSend, options)
| Parameter | Type | Description |
|---|---|---|
parentElement | string | Element | CSS selector or DOM element. Widget fills 100% of parent. |
onSend | function | Callback (chat, msg) => {} fired on Send click or Shift+Enter. |
options | object | Optional. { theme, trackHistory, titleArea: { title, show, align }, messagesArea: { alternating } } |
| Method | Description |
|---|---|
messageAddNew(content, user, align, role) | Add a message. Returns message ID. align: "left", "right", or "center". |
messageAddFull({content, userString, align, role, userID, visible, tags}) | Add a message with full options object. visible: false hides in DOM but keeps in history. tags: ["debug"] for group control. |
messageAppendContent(id, content) | Append to an existing message (streaming / token completion). |
messageReplaceContent(id, content) | Replace the content of an existing message. |
messageRemove(id) | Remove a message by ID. |
messageGetContent(id) | Get the content string of a message. |
messageGetDOMObject(id) | Get the DOM element of a message. |
messageAddTypingIndicator(user, align) | Add animated "..." typing indicator. Auto-cleared on append/replace. |
| Method | Description |
|---|---|
messageSetVisible(id, bool) | Show or hide a message. Hidden messages stay in history. |
messageGetVisible(id) | Returns true if message is visible. |
messageToggleVisible(id) | Toggle message visibility. |
messageSetVisibleByTag(tag, bool) | Show/hide all messages with a given tag. Returns count affected. |
messageGetTags(id) | Get the tags array for a message. |
messageSetTags(id, tags) | Set the tags array for a message. |
| Method | Description |
|---|---|
historyGet(n, m) | Get history entries. No args = all. historyGet(3) = entry 3. historyGet(2, 5) = entries 2–5. |
historyGetLength() | Number of messages in history. |
historyClear() | Clear all history and reset message IDs. |
historyExport() | Returns serializable array (no DOM refs). Safe for JSON.stringify. |
historyImport(data) | Restore messages from exported data. Clears existing messages first. |
| Method | Description |
|---|---|
titleAreaShow() / titleAreaHide() / titleAreaToggle() | Show, hide, or toggle the title bar. |
titleAreaSetContents(html, align) | Set title area HTML and alignment. |
titleAreaGetContents() | Get the title area innerHTML. |
inputAreaShow() / inputAreaHide() / inputAreaToggle() | Show, hide, or toggle the input area. |
inputAreaSetEnabled(bool) | Enable/disable the input field and send button. |
inputAreaSetButtonText(text) | Change the send button label. |
changeTheme(themeName) | Switch CSS theme at runtime. |
setDirection(dir) | Set text direction: 'ltr' or 'rtl'. |
getDirection() | Get current text direction. |
scrollToBottom() | Scroll messages area to the bottom. |
messagesAreaAlternateColors(bool) | Enable/disable alternating message row colors. |
messagesAreaShowTimestamps(bool) | Show/hide message timestamps. |
| Method | Description |
|---|---|
setMessageFormatter(fn) | Set a function to transform message content (e.g., markdown renderer). |
setSanitize(val) | true for built-in HTML escaping, or a (content) => cleaned function. |
| Method | Description |
|---|---|
setCallbackOnSend(fn) | Set or replace the onSend callback. |
setCallbackonMessageAdded(fn) | Fires after every message is added. (chat, msgId) => {} |
setCallbackonMessageAppend(fn) | Fires on messageAppendContent. (chat, msgId, content) => {} |
setCallbackonMessageReplace(fn) | Fires on messageReplaceContent. (chat, msgId, content) => {} |
setCallbackonMessageDelete(fn) | Fires on messageRemove. (chat, msgId) => {} |
| Method | Description |
|---|---|
quikchat.version() | Returns { version, license, url }. |
quikchat.loremIpsum(numChars, startSpot, capitalize) | Generate placeholder text. Useful for testing. |