Skip to main content
Integrate the Char agent into your HTMX application. The key is placing the agent outside your swap targets so it persists across partial page updates.

Installation

Basic Usage

Place the agent outside your hx-target area:
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/[email protected]"></script>
  <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js" defer></script>
</head>
<body>
  <!-- Navigation with hx-boost -->
  <nav hx-boost="true">
    <a href="/dashboard">Dashboard</a>
    <a href="/settings">Settings</a>
  </nav>

  <!-- Content that swaps -->
  <main id="content" hx-target="this" hx-swap="innerHTML">
    <!-- Page content loads here -->
  </main>

  <!-- Agent persists outside swap target -->
  <char-agent id="chat-widget"></char-agent>

  <script>
    const agent = document.getElementById('chat-widget');
    const idToken = document.querySelector('meta[name="id-token"]')?.content;
    const clientId = document.querySelector('meta[name="client-id"]')?.content;

    if (agent && idToken && clientId) {
      agent.connect({ idToken, clientId });
    }
  </script>
</body>
</html>

With hx-boost

When using hx-boost for SPA-like navigation, the agent stays connected:
<body hx-boost="true">
  <nav>
    <a href="/">Home</a>
    <a href="/dashboard">Dashboard</a>
  </nav>

  <main id="main">
    <!-- Boosted content swaps here -->
  </main>

  <!-- Agent outside main content area -->
  <char-agent id="chat-widget"></char-agent>
</body>

With Out-of-Band Swaps

If you need to update the agent from server responses, use hx-swap-oob:
<!-- Server response can include -->
<div id="chat-widget-container" hx-swap-oob="true">
  <char-agent id="chat-widget"></char-agent>
</div>

Python (Flask/Django)

Flask

from flask import Flask, render_template, session
import requests

app = Flask(__name__)

@app.route('/dashboard')
def dashboard():
    id_token = session.get('id_token')
    return render_template('dashboard.html', id_token=id_token)
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
  <meta name="id-token" content="{{ id_token }}">
  <meta name="client-id" content="{{ client_id }}">
  <script src="https://unpkg.com/[email protected]"></script>
  <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js" defer></script>
</head>
<body>
  <main id="content">
    {% block content %}{% endblock %}
  </main>

  <char-agent id="chat-widget"></char-agent>

  <script>
    const agent = document.getElementById('chat-widget');
    const idToken = document.querySelector('meta[name="id-token"]')?.content;
    const clientId = document.querySelector('meta[name="client-id"]')?.content;
    if (agent && idToken && clientId) agent.connect({ idToken, clientId });
  </script>
</body>
</html>

Django

# views.py
from django.shortcuts import render

def dashboard(request):
    context = {
        'id_token': request.session.get('id_token')
    }
    return render(request, 'dashboard.html', context)

Go (Templ/Echo)

// handler.go
func Dashboard(c echo.Context) error {
    idToken := getIDToken(c)
    return c.Render(http.StatusOK, "dashboard", map[string]interface{}{
        "IDToken": idToken,
    })
}
<!-- templates/base.html -->
<meta name="id-token" content="{{.IDToken}}">

See Also