Gothic Framework G symbol

Topics ⇆ HTMX Bridge

Topics and htmx meet in the core: an element can fire an htmx request when a Topic publishes, and an element can publish to a Topic after its swap. Put the two together and you get a full refresh loop with no hand-written JavaScript — two HTML attributes.

Topic → htmx: hx-trigger="topic:<name>"

Give an element hx-trigger="topic:<name>" and it runs its htmx request every time anything publishes to that Topic. Here a cart-summary panel refetches itself whenever the cart Topic changes:

<!-- Refetch the cart summary whenever ANYTHING publishes to the "cart" Topic. -->
<div
	hx-get="/cart/summary"
	hx-trigger="topic:cart"
	hx-swap="innerHTML"
>
	<!-- server-rendered cart summary lands here -->
</div>

Want to react to a single field? Use topic:<key>:<field> — for example topic:cart:qty. Under the hood, a Topic publish dispatches a gothic:topic:<name> DOM CustomEvent on document, and the alias maps the topic: trigger prefix onto it, so the wiring stays declarative.

<!-- Listen to a single field with topic:<key>:<field>. -->
<div hx-get="/cart/count" hx-trigger="topic:cart:qty" hx-swap="innerHTML"></div>

<!-- Under the hood a Topic publish dispatches a DOM CustomEvent on document:
       gothic:topic:cart          (whole-struct publish)
       gothic:topic:cart:qty      (single-field publish)
     The alias maps the "topic:" trigger prefix onto "gothic:topic:", so you
     write topic:cart and htmx listens for gothic:topic:cart. No JS. -->

htmx → Topic: hx-publish="topic:<name>"

The other direction: give an element hx-publish="topic:<name>" and, after its htmx swap completes, it publishes to that Topic. It is notify-style — it broadcasts the name and carries no value, which is exactly what a subscriber's hx-trigger="topic:<name>" is waiting for.

<!-- After this button's htmx swap completes, publish to the "cart" Topic.
     hx-publish is notify-style: it broadcasts the name, it carries no value. -->
<button
	hx-post="/cart/add"
	hx-swap="outerHTML"
	hx-publish="topic:cart"
>
	Add to cart
</button>

The complete no-JS loop

Wire a publisher and a subscriber to the same Topic name and the loop closes on its own: the button adds an item and publishes cart; the summary hears cart and refetches. Any number of panels can subscribe to the same Topic — they all refresh together.

<!-- A complete refresh loop, no hand-written JavaScript. -->

<!-- Publisher: broadcasts "cart" after it adds an item. -->
<button hx-post="/cart/add" hx-swap="outerHTML" hx-publish="topic:cart">
	Add to cart
</button>

<!-- Subscriber: refetches itself whenever "cart" is published. -->
<div hx-get="/cart/summary" hx-trigger="topic:cart" hx-swap="innerHTML">
	<!-- cart summary -->
</div>

Ready to share code across WASM pages and render templ components client-side? That's next!