Recipes

May 4, 2026 · View on GitHub

Common patterns and quick-start snippets for QuikChat.


Read-Only Display

Hide the input area and omit the callback to create a display-only widget:

const display = new quikchat('#container', null, {
  titleArea: { title: 'Announcements', show: true }
});
display.inputAreaHide();
display.messageAddNew('System maintenance at 2 AM UTC.', 'admin', 'left');

Log Viewer with Tag Filtering

Use messageAddFull() with tags and messageSetVisibleByTag() for filtered views:

const log = new quikchat('#log', null);
log.inputAreaHide();

log.messageAddFull({ content: 'Connected to DB', userString: 'INFO', role: 'info', tags: ['info'] });
log.messageAddFull({ content: 'Disk usage at 90%', userString: 'WARN', role: 'warn', tags: ['warn'] });
log.messageAddFull({ content: 'Connection refused', userString: 'ERROR', role: 'error', tags: ['error'] });

// Show only errors:
log.messageSetVisibleByTag('info', false);
log.messageSetVisibleByTag('warn', false);
log.messageSetVisibleByTag('error', true);

Style log levels with role-based CSS: .quikchat-role-error, .quikchat-role-warn, etc. See the Log Viewer example.


Tool-Call Visibility Toggle

Hide tool calls from the user, then reveal them on demand:

// Add a hidden tool-call message
chat.messageAddFull({
  content: '{"tool": "search", "query": "flights"}',
  userString: 'Tool',
  role: 'tool',
  visible: false,
  tags: ['tool-call']
});

// Toggle all tool-call messages
let showing = false;
document.getElementById('toggle').onclick = () => {
  showing = !showing;
  chat.messageSetVisibleByTag('tool-call', showing);
};

See the Tool-Call Visibility example.


Session Persistence with localStorage

Export the session, store it, and restore it on next visit:

// Save
function saveSession() {
  const data = chat.historyExport();
  localStorage.setItem('chat-session', JSON.stringify(data));
}

// Restore
function restoreSession() {
  const raw = localStorage.getItem('chat-session');
  if (raw) {
    chat.historyImport(JSON.parse(raw));
  }
}

// Auto-save on every new message
chat.setCallbackonMessageAdded(() => saveSession());

// Restore on page load
restoreSession();

See the Session Save/Restore example.


RTL Support

Switch the entire widget to right-to-left layout:

const chat = new quikchat('#chat', onSend, {
  titleArea: { title: 'محادثة', align: 'right', show: true }
});
chat.setDirection('rtl');

// Toggle at runtime
chat.setDirection(chat.getDirection() === 'rtl' ? 'ltr' : 'rtl');

See the RTL / i18n example.


Custom Sanitizer (DOMPurify)

Replace the built-in HTML escaping with DOMPurify for richer content:

<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
<script>
const chat = new quikchat('#chat', onSend);
chat.setSanitize((content) => DOMPurify.sanitize(content));
</script>

The sanitizer runs before the markdown plugin, so it processes raw user input.


Streaming LLM Responses

The standard pattern for token-by-token display:

const chat = new quikchat('#chat', async (chat, msg) => {
  chat.messageAddNew(msg, 'user', 'right', 'user');
  chat.inputAreaSetEnabled(false);

  const id = chat.messageAddTypingIndicator('bot');

  // ... read tokens from your stream ...
  chat.messageReplaceContent(id, firstToken);   // clears dots
  chat.messageAppendContent(id, nextToken);      // append subsequent tokens

  chat.inputAreaSetEnabled(true);
});

See the Streaming guide for complete fetch + ReadableStream examples.


Disable Input During Response

Prevent the user from sending while the bot is thinking:

const chat = new quikchat('#chat', async (chat, msg) => {
  chat.messageAddNew(msg, 'user', 'right');
  chat.inputAreaSetEnabled(false);
  chat.inputAreaSetButtonText('Thinking...');

  const reply = await getReply(msg);
  chat.messageAddNew(reply, 'bot', 'left');

  chat.inputAreaSetEnabled(true);
  chat.inputAreaSetButtonText('Send');
});