OPEN SOURCE · MIT
Compiles to the vanilla JavaScript you would have written
AmoJS is a build-time compiler for fine-grained reactive UI. Signals and
html`` templates in standard .js files — no custom file
format, no virtual DOM. It runs in the browser with no build step; the compiler is an
optimizer you add when you ship.
import { signal, html, mount } from '@amojs.dev/core';
const count = signal(0);
const bump = () => count.value++;
const Counter = () =>
html`<button onclick=${bump}>${count}</button>`;
mount(Counter, document.body);
import { signal, mount } from '@amojs.dev/core/runtime';
import { tpl as _$t, bindChild as _$child, bindEvent as _$event } from "@amojs.dev/core/compiled";
const _t0 = _$t("<button><!----></button>", [[0,0]]);
const count = signal(0);
const bump = () => count.value++;
const Counter = () =>
(() => {
const _r = _t0().firstChild;
const _n0 = _r.firstChild;
_$child(_n0, count);
_$event(_r, "click", bump);
return _r;
})();
mount(Counter, document.body);
HOW IT WORKS
Less framework. More JavaScript.
Write standard .js
Signals for state, tagged templates for markup. No custom file format, no JSX transform, no hook rules. Your app runs in the browser with no build step at all.
Compile when you ship
amo build csr | ssr | ssg replaces the template machinery with precise, pre-computed DOM operations. Each hole becomes one targeted update; the template parser ships nowhere.
Ship direct DOM code
One effect per binding, one write per change. No virtual DOM, no diffing, no tree walks — the same code a careful developer would have written by hand.
$ npx amo eject src dist amo eject — 2 compiled, 10 copied, 9 runtime files → dist (runtime: amo-runtime/) runtime taken from node_modules/@amojs.dev/core/src no bare "@amojs.dev/core" imports remain — deleting amo changes nothing.
THE EXIT IS PART OF THE DESIGN
amo eject
One command compiles your app a final time, hands the runtime over as readable source files, and rewrites every import to a relative path. What remains is a vanilla JavaScript project you own completely.
Nothing about your architecture changes — because compiled AmoJS already was vanilla JavaScript.
THE WHOLE API
Twelve names. That's the surface.
signal()reactive statecomputed()derived valueseffect()reactionsisSignal()is this reactive?flushSync()run pending effects nowtick()await the batchroot()a detached scopeonCleanup()teardownonMount()after insertionhtmltagged templatesmount()attach to the DOMeach()keyed listsWrite JavaScript. Ship JavaScript.
Runs in the browser today, no build step. Compile when it counts.