v1.4 · Released Mar 14, 2026 · MIT

HTML is the API. Always was.

Heron is an 8kb framework for hypermedia apps. Drive your UI with a handful of HTML attributes — server-rendered, no build step, no bundler, no virtual DOM.

terminal
npm
$ npm install heron

Zero dependencies · Works with any backend · 8kb gzipped

Trusted in production at

Northwind Cobalt Driftwood Maple Tessera Harbor Lumen Labs Verdant Quill

The whole idea, in twelve lines

Search-as-you-type. No JavaScript.

The server sends HTML. The browser swaps it in. Heron is the thin layer of attributes between those two facts — nothing more.

search.html
1<!-- The input asks the server on every keystroke -->
2<input type="search" name="q" placeholder="Search the docs…"
3       hx-get="/search"
4       hx-trigger="keyup changed delay:200ms"
5       hx-target="#results"
6       hx-indicator="#spin" />
7
8<!-- A spinner Heron shows while the request is in flight -->
9<span id="spin" class="heron-indicator">searching…</span>
10
11<!-- The server's HTML response lands right here -->
12<ul id="results"></ul>
hx-get — the URL to fetch hx-trigger — when to fetch it hx-target — where the HTML goes

That is the entire feature. No state store, no client router, no rebuild on save.

Why Heron exists

Most apps were never single-page apps.

Roughly four in five web apps are content-driven: a dashboard, a catalogue, a settings page, an admin panel. They navigate, they submit forms, they refresh a fragment. Hypermedia — links and forms that return HTML — already describes that shape perfectly. It has since 1993. Heron's whole bet is that you can extend that model with a thin layer of attributes instead of throwing it away for a client-side runtime.

A React SPA pays a fixed tax for every one of those pages: a bundler, a build step, a hydration pass, a state library, an API layer that exists only to feed the client, and a second copy of your routing logic. For a genuinely interactive surface — a spreadsheet, a canvas editor — that tax buys something real. For a pricing page or an issue tracker, you have paid 240kb to re-implement what an anchor tag did for free.

HTMX proved the hypermedia approach at scale, and Heron owes it everything. Heron is the leaner sibling: 8kb gzipped instead of 14, eleven attributes instead of forty, and stricter defaults — requests are scoped, swaps are explicit, and history is opt-in. Fewer knobs, fewer footguns, the same idea. If you outgrow it, you outgrow it onto plain HTML, not onto a migration.

Things we don't do

Heron is defined as much by its omissions as its features.

  • No virtual DOM. The browser's DOM is the only tree. Heron swaps real nodes.
  • No client-side state store. Your database is the state. The page is a view of it.
  • No build step. One script tag, or one npm import. Nothing to compile, nothing to watch.
  • No JSX or template DSL. You write HTML. Your server writes HTML. That's the contract.
  • No opinion on your backend. Rails, Go, Django, PHP, a flat file — Heron never asks.

What you get

Eleven attributes. Eight kilobytes.

Every feature below is reachable from an HTML attribute. There is no imperative API to learn and no escape hatch you'll be forced into.

Server-rendered, always

Your server owns every byte of markup. Pages are crawlable, printable, and viewable with JavaScript disabled — Heron only enhances what's already there.

return render("results.html", rows)

No build step

Drop in one script tag and ship. No transpiler, no dev server to keep alive, no source maps to debug. The file you write is the file the browser runs.

<script src="heron.min.js"></script>

No bundler in sight

Webpack, Vite, esbuild, Rollup — Heron needs none of them. Dependency graphs, tree-shaking and chunk-splitting are problems you simply no longer have.

# node_modules: 1 package

8kb gzipped, audited

The entire runtime is 8,192 bytes over the wire — smaller than most hero images. A CI gate fails the build if a commit pushes it past that line.

heron.min.js.gz → 8.0 kB

Framework-agnostic backend

Heron speaks plain HTTP and reads plain HTML. Pair it with Rails, Laravel, Express, Flask, ASP.NET or a static generator — no adapter, no SDK.

GET /search → 200 text/html

Declarative attributes only

Behaviour lives in markup, next to the element it affects. Read an HTML file top to bottom and you have read the whole feature — no jumping to a controller.

hx-post="/cart" hx-swap="outerHTML"

How it compares

The same app, a fraction of the weight.

Numbers below are for an equivalent CRUD admin panel, measured at the same feature parity.

Heron React HTMX Hotwire
Runtime size (gzipped) 8 kb 240 kb 14 kb 39 kb
Build step required
None
Webpack
None
esbuild
Authoring model HTML attrs JSX HTML attrs HTML + Stimulus
SSR-native (no hydration)
Works with no JS (graceful)
Distinct API surface 11 attrs ~120 hooks/APIs ~40 attrs ~30 + Stimulus
Time to first interaction* 0.21s 1.18s 0.34s 0.52s

* P95, cold cache, simulated Fast 3G, 4× CPU throttle — Lighthouse 12, median of 28 runs against the reference admin panel.

Quick start

Hello world in under 60 seconds.

Three steps. No scaffolding command, no project template, no account. Add Heron to a page you already have.

1

Add Heron to the page

Install via npm, or for a no-build site, drop the CDN tag straight into your <head>.

terminal
$ npm install heron
2

Mark up an interaction

Add three attributes to a button. It now asks the server for HTML and swaps the reply in place.

index.html
<button hx-get="/quote" hx-target="#out" hx-swap="innerHTML">
  Get a quote
</button>
<div id="out"></div>
3

Return an HTML fragment

Your route replies with a snippet of HTML — not JSON. Heron places it exactly where you pointed it.

server.js
app.get("/quote", (req, res) => {
  res.send("<p>That will be $48/mo.</p>");
});

That's a working interaction. No bundler ran, no client state, no page reload — elapsed: about forty seconds.

Prefer no npm at all?

Skip step 1 entirely. Paste this one tag into your page's <head> and you're done — no install, no build.

no-build · CDN
<script src="https://unpkg.com/[email protected]"></script>

Community

Built in the open, answered fast.

Heron is maintained by a small team and a wide circle of contributors. Questions get real answers — most discussion threads close within a day.

GitHub Discussions

2,941

threads · 94% answered within 24h

Discord — online now

6,182

members · #help, #showcase, #internals

Contributors

148

across 36 countries · 11 with commit access

Core maintainers

EM

Esther Maguire

@esmag · lead

RT

Rafael Tavares

@rtav · core

AK

Anjali Khanna

@anjk · docs

LN

Lukas Nielsen

@lukn · runtime

Heron has no company behind it.

It runs on GitHub Sponsors. If your business ships on it, fund a maintainer day.

Become a sponsor

Sponsors

Funded by people who depend on it.

Every sponsorship goes directly to maintainer hours, security review, and the documentation site. Thank you.

Organisation sponsors

Vercel
Cloudflare
Sentry
Linear

47 individual sponsors

MI TP NR SA PD CW HS DS YH OL FN GB KM LF AS BC +31

FAQ

The questions people actually ask.

Is this production-ready?

Yes. Heron has been on a stable 1.x line since October 2025 and follows strict semver — no breaking change ships outside a major. It powers 4,408 sites we can see in the wild, including the Northwind and Driftwood admin panels above. The runtime carries a 600-case test suite run against every supported browser, and security-relevant fixes are backported to the previous minor for twelve months.

Why not just use HTMX?

HTMX is excellent, and Heron exists because of it — please use HTMX if it fits. Heron is a deliberately narrower take: eleven attributes instead of roughly forty, 8kb instead of 14, and stricter defaults so there are fewer ways to surprise yourself. Requests are scoped to their element, swaps must be named explicitly, and history is opt-in. If you want the broader, more configurable toolkit, stay on HTMX. If you want the smallest possible hypermedia layer with sharp edges sanded off, that's Heron.

Does it work with React / Vue?

It can coexist, though that isn't the design goal. Heron only touches elements carrying hx- attributes, so you can keep a React or Vue island elsewhere on the page and Heron will never reach inside it. Many teams use Heron for the content-driven 80% of a product and reserve a framework for the genuinely interactive 20% — a canvas, a complex editor. What we don't recommend is rendering a React tree and then asking Heron to swap nodes inside it; pick one owner per region of the DOM.

Bundle size guarantees?

The 8kb figure is enforced, not aspirational. Every pull request runs a size check in CI; if the gzipped runtime would cross 8,192 bytes, the build fails and the PR cannot merge until it's back under budget. The number is published on each release and has not moved since 1.0. Heron also ships zero runtime dependencies, so your lockfile gains exactly one line and there is no transitive tree to audit.

Long-term maintenance commitment?

Heron is MIT-licensed and governed by a four-person maintainer team plus 148 contributors, so it does not depend on any single person. Funding comes through GitHub Sponsors from organisations including Vercel, Cloudflare, Sentry and Linear, which pays for steady maintainer hours rather than a launch-and-leave release. The 1.x line has a published support window through at least 2029, and because the API is just HTML attributes, the worst-case migration path is plain HTML you already own.

Get started in 60 seconds.

One install, three attributes, a page you already have. Heron stays out of your way and ships nothing you didn't write.

terminal npm · 8kb
$ npm install heron

28,408

GitHub stars

148,408

npm / week

Stars · 12 months