Copy the code below into test.html, open it in your browser, and you have a reactive app running from a static file. No build step. No npm install. No server. Just HTML and a 2.9 kB library.
The Problem: Reactivity Shouldn't Require a Framework
Modern web development has a secret: to get simple reactivity—a counter, a todo list, a form that updates live—you often need:
- A build tool (Vite, Webpack)
- A package manager (npm, yarn)
- A framework (React, Vue, Angular)
- A state management library (Redux, Pinia)
- 40+ kB of JavaScript
And after all that, you still have to learn JSX, hooks, lifecycle methods, and a dozen other concepts.
For many developers—especially those in PHP, WordPress, or legacy environments—this is overkill. They just want a button that updates a count, a list that adds items, a form that feels alive.
That's where HTMP comes in.
The Solution: HTMP — HyperText Mutation & Projection
HTMP is a zero-dependency, browser-native reactive UI engine built on three pillars:
Pattern – your HTML blueprint, parsed once by the browser's DOMParser
Proxy – reactive state via native Proxy
Program – your methods and event handlers
It weighs 2.9 kB gzipped. It has no dependencies. It works in any browser. And you can run it from a file:// URL.
No build. No npm. No framework.
Try It Yourself (30 Seconds)
Create a file called test.html on your desktop.
Paste the code below into it.
Save and double-click the file.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: sans-serif; padding: 20px; }
.done { text-decoration: line-through; color: gray; }
button { cursor: pointer; margin: 2px; }
</style>
</head>
<body>
<button onclick="app.unmount()">Unmount App</button>
<button onclick="app.remount()">Remount App</button>
<div id="app"></div>
<script type="module">
import { HTMP } from 'https://unpkg.com/htm-projection@latest/dist/esm/index.js';
const pattern = `
<div>
<h1>{{ title }}</h1>
<input type="text" @input="changeTitle(e)" placeholder="Type title..." />
<p>Count: {{ count }}</p>
<button @click="increment()">Increment</button>
<hr>
<p><i>{{ todos.length === 0 ? 'No todos yet. Add one below!' : '' }}</i></p>
<input type="text" @input="changeInput(e)" :value="inputTodo" placeholder="New task..." />
<button @click="addTodo()">Add Todo</button>
<ul>
<li :for="todo in todos">
<span :class="todo.done ? 'done' : ''">{{ todo.text }}</span>
<button @click="finishTodo(todo)">{{ todo.done ? 'Undo' : 'Finish' }}</button>
<button @click="deleteTodo(todo)">Delete</button>
</li>
</ul>
</div>
`;
const app = new HTMP('app', pattern);
app.setProxy({
title: "",
count: 0,
inputTodo: '',
todos: []
});
app.setProgram({
changeTitle: (e) => { app.proxy.title = e.target.value; },
increment: () => { app.proxy.count++; },
changeInput: (e) => { app.proxy.inputTodo = e.target.value; },
addTodo: () => {
if (!app.proxy.inputTodo.trim()) return;
app.proxy.todos = [...app.proxy.todos, { id: Date.now(), text: app.proxy.inputTodo, done: false }];
app.proxy.inputTodo = '';
},
finishTodo: (todo) => {
app.proxy.todos = app.proxy.todos.map(t => t.id === todo.id ? { ...t, done: !t.done } : t);
},
deleteTodo: (todo) => {
app.proxy.todos = app.proxy.todos.filter(t => t.id !== todo.id);
}
});
app.mount();
window.app = app;
</script>
</body>
</html>
Open it in your browser. You'll see:
A title that updates as you type.
A counter that increments.
A todo list that adds, finishes, and deletes items.
Buttons to unmount and remount the entire app—without losing state.
All from a static file. No server. No build.
Why This Works (The 3 Pillars)
P1 Pattern — The HTML Blueprint
HTMP parses your HTML string once using the browser's native DOMParser. Every dynamic binding— {{ }}, :for, :class, @click —is registered with a path to its DOM node.
<li :for="todo in todos">
<span :class="todo.done ? 'done' : ''">{{ todo.text }}</span>
</li>
No JSX. No compiler. Just HTML.
P2 Proxy — The Reactive State
State lives in a Proxy object. When you change a property, HTMP knows exactly which bindings need updating.
app.proxy.todos = [...app.proxy.todos, newTodo];
No diffing. No virtual DOM. Just a proxy trap.
P3 Program — Your Methods
Event handlers are plain JavaScript functions. No hooks, no lifecycle methods.
app.setProgram({
addTodo: () => {
app.proxy.todos = [...app.proxy.todos, newTodo];
}
});
No useState. No useEffect. Just functions.
The Magic: Slot-Based Rendering
Notice this line in the pattern:
<p><i>{{ todos.length === 0 ? 'No todos yet. Add one below!' : '' }}</i></p>
In React or Vue, you'd need a conditional (v-if, {condition && <p>}) to avoid rendering an empty element. In HTMP, the pattern is always there—the expression just evaluates to an empty string. The <p> remains, but it's invisible because it has no content.
No conditional rendering needed. The template is stable. State fills the slots.
The Bonus: Unmount & Remount
HTMP includes a lifecycle: mount(), unmount(), remount(), destroy().
In the example, the buttons let you unmount the app (remove it from the DOM) and remount it (put it back)—without losing state. This is perfect for SPAs or for widgets that need to be hidden and shown.
How It Compares
| Feature | React | Vue | HTMX | HTMP |
|---|---|---|---|---|
| Build step | Yes | Optional | No | No |
| npm install | Yes | Yes | No | No |
| Bundle size | ~40 kB | ~30 kB | ~14 kB | 2.9 kB |
| Dependencies | Many | Many | 0 | 0 |
| Reactivity | Yes | Yes | No | Yes |
| Works from file:// | No | No | Yes | Yes |
| Learning curve | High | Medium | Low | Very Low |
HTMP is smaller than HTMX and gives you true client-side reactivity—without a build step.
Who Is This For?
PHP / Laravel / CodeIgniter developers who want reactivity without leaving their stack.
WordPress developers who need interactive widgets in a legacy environment.
jQuery users who want to modernize without learning React.
Anyone who needs a small, fast, reactive UI for a widget, a form, or a simple SPA.
AI agents – HTMP is so intuitive that LLMs can generate and refactor it without prior training.
Try It Live
Want to see more? These examples run entirely in your browser:
Basic Example – Title editor, counter, todo list.
Multi-Step Form – Step-by-step form with validation.
SPA Ticket App – Routing with History API and 404 fallback.
Note Guard with Polaris Runtime – Integration with a workflow engine.
Get Started
npm install htm-projection
Or use it directly from CDN:
<script type="module">
import { HTMP } from 'https://unpkg.com/htm-projection@latest/dist/esm/index.js';
</script>
GitHub: github.com/erlanggasatria-source/HTMP
npm: npmjs.com/package/htm-projection
BundlePhobia: 2.9 kB gzipped, zero dependencies.
The Bottom Line
Reactivity should not require a framework.
HTMP proves that you can have React-like reactivity with just HTML, a tiny proxy-based engine, and no build step. It's not a replacement for React or Vue in large applications—but for the thousands of smaller projects, widgets, and legacy integrations, it's a breath of fresh air.
Copy the code, save it as test.html, open it. That's it.
Welcome to reactivity without the weight.
Top comments (0)