Bendik Aagaard LynghaugandClaude Fable 5 b957b02410 docs: Phase 5 — v8 README, migration guide, deploy recipes, RFC draft
README rewritten for the v8 branch: honest size claim (7.97 kB min+gzip
for the whole framework, with the v7 '4kb' context), the zero-build
story up front, bankai v10, package map, credits to yoshuawuyts, the
choojs contributors and the pirxpilot fork line.

docs/migrating-v7-to-v8.md: the codemod path, the specifier map, and
every deliberate behavior change spelled out. docs/deploy.md: plain
Node, Docker, proxy/CDN — including the HTTP/3 answer (h3 is
infrastructure's job; bankai's contract is the 103 + Link headers that
any hints-aware edge, h3 included, propagates; server push is dead
everywhere and never existed in h3) — and web-standard runtimes.

docs/rfc.md: the draft announcement for choojs/choo — continuation
framing, pings to yoshuawuyts and pirxpilot, the npm-rights ask, API
feedback questions, and a three-week comment window with a
silence-is-consent close.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014NgfSjHE11oFpoSnLVKLXd
2026-09-08 19:41:22 +02:00
2018-03-30 18:19:06 +02:00
2018-08-01 12:32:36 +02:00
2018-04-04 12:28:49 +02:00
2017-11-24 13:44:11 +01:00
2017-09-20 21:12:37 +02:00
2019-08-14 10:48:33 +02:00
2019-08-05 15:44:06 +02:00
2020-01-23 18:11:06 +01:00
2020-01-23 18:11:06 +01:00
.
2016-05-11 01:09:53 +07:00

choo

🚂🚋🚋🚋🚋🚋
The sturdy frontend framework — now with a modern engine under the same hood.

This is the v8 branch. choo v7 is on master and stays untouched until 8.0.0 ships. Everything below describes v8: same API, same philosophy, rebuilt for the platform of 2026. See docs/v8.md for status and the full plan.

What is this

Choo is a small framework for building frontend applications with plain JavaScript and HTML. State lives in stores, changes flow through an event emitter, views are tagged template literals, and re-renders morph the real DOM. The whole framework — core, html engine, morphing, hydration, router, event bus — is 7.97 kB min+gzip (7.15 kB brotli), enforced by CI.

import choo from '@choojs/core'
import html from '@choojs/html'

const app = choo()

app.use((state, emitter) => {
  state.count = 0
  emitter.on('increment', (n) => {
    state.count += n
    emitter.emit('render')
  })
})

app.route('/', (state, emit) => html`
  <body>
    <h1>count is ${state.count}</h1>
    <button onclick=${() => emit('increment', 1)}>Increment</button>
  </body>
`)

app.mount('body')

Why v8

  • No build step, anywhere. Templates parse at runtime (cached per call site in a WeakMap, clone-based instantiation — as fast as the old compile-time transform, without the toolchain). An import map and a <script type="module"> is a complete development setup: see examples/counter/index.html.
  • Isomorphic by contract. One app module. The browser mounts it, the server imports the same file and calls toString(route) or streams it with toStream(route) — a web-standard ReadableStream, so it plugs into Node, Deno, Bun, and edge runtimes alike.
  • Streaming SSR. Async values in child position flush the shell first and stream the rest in document order. Store data via state.prefetch promises. Hydration adopts the server DOM (same element references, form state kept) and warns precisely when server and client markup disagree.
  • Async routes, finally. app.route('/big', lazy(() => import('./big.js'))) — loading view or held tree while in flight, awaited on the server. (choojs/choo#653, answered.)
  • Modern platform, no shims. ESM only, Node ≥ 24, Baseline Widely Available browsers. The router speaks WHATWG URL and survives emoji, literal %, and Unicode normalization differences.

bankai v10

The isomorphic compiler & server, rebuilt on Vite 8 / Rolldown. Only the client ever bundles — v8 server code is plain ESM Node runs as-authored.

$ bankai start app.js     # dev: HMR + streaming SSR on every request
$ bankai build app.js     # client bundle, manifest, service worker,
                          # brotli/gzip precompression, --prerender
$ bankai serve            # prod: 103 Early Hints, immutable assets,
                          # streaming SSR, --h2
$ bankai inspect          # raw/gzip/brotli size report

Conventions over config: style.css next to your entry is bundled for the client (server code never sees it), sw.js becomes a service worker with the precache manifest injected, <title> follows your DOMTitleChange emits — on the server too.

Packages

package what it is
@choojs/core the Choo class: stores, router, emitter, toString/toStream, lazy()
@choojs/html tagged templates: DOM in the browser, strings/streams on the server, morph, hydrate, raw
@choojs/component stateful components with lifecycle hooks; the hydration boundary
@choojs/devtools window.choo console tooling
@choojs/migrate choo-migrate — the v7 → v8 codemod
bankai the compiler & server above

Migrating from v7

$ npx @choojs/migrate .

converts simple CJS, remaps every specifier, and tells you honestly what it didn't dare touch. The API surface is unchanged — choo() still works without new. See docs/migrating-v7-to-v8.md.

Development

$ npm install
$ npm test              # 109 tests on node:test, zero framework deps
$ npm run test:e2e      # real Chromium: hydration, streaming, bankai
$ npm run size          # the wire-size budget
$ npm run bench         # vs nanohtml v1 and µhtml

Credits

Choo v8 carries forward a decade of work by Yoshua Wuyts and the choojs contributors, and builds on the ESM groundwork of the @pirxpilot fork line. Per-file attribution headers name the module and version each port came from. MIT, as always.

S
Description
A modern fork of choo using vite
Readme MIT
873 KiB
Languages
JavaScript 99.7%
TypeScript 0.3%