A zero-dependency JavaScript chat widget you can drop into any page.
11 themes, XSS sanitization, streaming, session save/restore, dynamic resize, RTL support — under 5 KB gzipped.
No framework required. Works great with
React,
Vue,
Solid,
Svelte, and
Angular too.
QuikChat is a zero-dependency chat widget. The base build handles messages, themes, streaming, history, and more in ~5 KB.
Add the quikchat-md-full build (still one <script> tag, still zero npm dependencies) and each message is rendered through
quikdown — adding tables, code highlighting, math, interactive maps, Mermaid diagrams, and inline SVG.
For just basic markdown use the lighter quikchat-md build (~9 KB).
For a pure chat widget with no markdown, see Getting Started.
Scroll through the conversation below to see what the full build can do. See also: examples for history save/restore, tool-call visibility, virtual scrolling, RTL, and more.
<link rel="stylesheet" href="https://unpkg.com/quikchat/dist/quikchat.css">
<script src="https://unpkg.com/quikchat/dist/quikchat-md-full.umd.min.js"></script>
<div id="chat" style="height: 480px;"></div>
<script>
const chat = new quikchat("#chat",
(chat, msg) => {
chat.messageAddNew(msg, "you", "right", "user");
// send msg to your LLM, stream the response back:
// chat.messageAppendContent(id, token);
},
{ theme: "quikchat-theme-light",
titleArea: { title: "My Chat", show: true } }
);
</script>
Three builds available: base (~5 KB, no markdown), md (~9 KB, basic markdown), md-full (~14 KB, + syntax highlighting, math, maps, diagrams). All are a single script tag with zero npm dependencies.
historyGet() returns OpenAI-compatible { role, content } objectsQuikChat sits between "build it from scratch" and "install a full chat SDK." Here's how it compares:
| Feature | QuikChat | Custom <div> | Full Chat SDK |
|---|---|---|---|
| Bundle size | ~5 KB gzip | 0 KB | 50-200 KB |
| Dependencies | 0 | 0 | 5-20+ |
| Setup time | 1 <script> tag | Hours of CSS/JS | npm + config |
| Themes | 11 built-in | DIY | Varies |
| Markdown | Built-in option | No | Plugin |
| XSS sanitization | Built-in | No | Varies |
| History export | historyExport() | No | Custom |
| Streaming | messageAppendContent() | No | Yes |
| RTL support | setDirection('rtl') | No | Varies |
Themes are CSS classes applied to the widget root. Switch themes at runtime:
chat.changeTheme("quikchat-theme-dark");
Eleven themes are included: light, blue, warm,
dark, midnight, ocean, modern,
glass, gradient, minimal, 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. |