Modern frontend development often assumes that building interactive web interfaces requires a heavy JavaScript framework. Over the years, frameworks have grown increasingly complex, introducing components, state management systems, build pipelines, and large runtime bundles. https://github.com/eringen/talkdom
But the web already has a powerful platform: HTML + the DOM + HTTP.
The idea behind talkDOM is simple:
What if DOM elements could send messages to each other?
Instead of wiring JavaScript handlers everywhere, HTML becomes a small message-passing language. Elements act as senders and receivers, and interactions are expressed declaratively.
The result is a tiny runtime (only a few kilobytes) that enables fetching data, updating DOM fragments, composing pipelines, polling endpoints, and controlling navigation — all from HTML.
The Core Idea
In talkDOM, elements communicate using Smalltalk-style messages.
A sender contains a sender attribute:
<button sender="posts get:/posts | list apply:inner">
Load Posts
</button>
And somewhere in the page there is a receiver:
<div receiver="list"></div>
When the button is clicked:
- A message is sent to
posts - A GET request is performed
- The response is piped to
list - The list's content is replaced
This message:
posts get:/posts | list apply:inner
can be read almost like a sentence:
"Posts, get /posts, then list apply the result as inner content."Message Pipelines
One of the key features of talkDOM is message pipelines.
Multiple operations can be composed using |:
posts get:/posts | transform apply:json | list apply:inner
Each step can return a value (or a promise), which is passed to the next step in the pipeline.
This design is inspired by:
* Unix pipes * Smalltalk keyword messages * message passing systems
Pipelines allow complex UI behaviors to be expressed with surprisingly little code.
Receivers
Receivers are simply DOM elements with a receiver attribute.
<div receiver="posts"></div>
Receivers can also belong to multiple groups:
<div receiver="toast error"></div>
Messages sent to toast will target all matching elements.
Receivers can optionally restrict what operations they accept:
<div receiver="posts" accepts="inner append text"></div>
This adds a small layer of safety to DOM updates.
Fetching and Updating Content
talkDOM provides built-in methods for HTTP requests and DOM updates.
Example:
<button sender="posts get:/posts apply:inner">
Load
</button>
<div receiver="posts"></div>
Supported request methods:
* get:
* post:
* put:
* delete:
And update operations:
* inner
* append
* text
* outer
Server-Triggered Actions
Servers can trigger client-side actions using a response header:
X-TalkDOM-Trigger
Example response header:
X-TalkDOM-Trigger: toast apply:"Saved!" text
This allows the server to instruct the client to update UI elements without embedding JavaScript.
Polling
Receivers can automatically poll an endpoint.
<div receiver="feed get:/posts poll:5s"></div>
This will fetch /posts every 5 seconds and update the receiver.
Polling stops automatically if the element is removed from the DOM.
Navigation and History
talkDOM also supports history-aware navigation.
<a sender="page get:/posts apply:inner" push-url="/posts">
Posts
</a>
When clicked:
- The request is executed
- The content is updated
- The URL is pushed to browser history
Back/forward navigation replays the same message chain.
Persistence
Receivers can optionally persist their content in localStorage.
<div receiver="cart" persist></div>
After page reload, the content is restored automatically.
Events
talkDOM emits lifecycle events after operations:
* talkdom:done
* talkdom:error
Example:
document.addEventListener("talkdom:done", e => {
console.log("operation finished", e.detail);
});
This makes it easy to hook into the runtime when needed.
Why Build This?
Projects like HTMX, Unpoly, and Turbo have shown that many interactive interfaces can be built without large frontend frameworks.
talkDOM explores a slightly different direction.
Instead of attribute-based APIs like:
hx-get="/posts"
hx-target="#list"
hx-swap="inner"
talkDOM uses a message syntax:
posts get:/posts apply:inner
This makes interactions:
* composable * pipeline-friendly * closer to a programming language
HTML becomes a small DSL for UI behavior.
Design Goals
talkDOM was built with a few goals in mind:
* Tiny runtime * No build step * Progressive enhancement * Composable interactions * Minimal JavaScript
The entire runtime is just a few hundred lines of JavaScript.
What This Is (and Isn’t)
talkDOM is not trying to replace full UI frameworks.
It is best suited for:
* server-rendered applications * dashboards * admin panels * progressively enhanced pages
Where the server remains the primary source of truth.
Closing Thoughts
The web already provides a powerful programming model:
* documents * events * HTTP * the DOM
Sometimes the best tool is not another abstraction layer, but a small runtime that lets the platform speak for itself.
talkDOM is an experiment in that direction: turning HTML into a tiny message-passing language for building interactive interfaces.
The project is available on GitHub:
https://github.com/eringen/talkdom
Also currently in use in here!