Skip to content

Repository files navigation

@async/framework

Async is a layered framework plan that starts as a no-build browser bootloader: signals, async signals, delegated command events, scoped fragment components, server calls, route partials, and out-of-order boundary swaps without a virtual DOM.

pnpm add @async/framework
<mainasync:container><buttontype="button" on:click="decrement">-</button><strongsignal:text="count"></strong><buttontype="button" on:click="increment">+</button></main><scripttype="module" src="./main.js"></script>
import{Async,createSignal}from"@async/framework";Async.use({signal: {count: createSignal(0)},handler: {increment(){this.signals.update("count",(count)=>count+1);},decrement(){this.signals.update("count",(count)=>count-1);}}});Async.start({root: document});

Why Async

Async keeps the browser path small and explicit:

  • Native HTML remains the document contract.
  • Signals are the state boundary.
  • Async.use(...) registers app declarations before or after startup.
  • Handlers run through delegated DOM events.
  • Async signals use native AbortSignal cancellation.
  • Browser and server cache declarations are structurally split.
  • Boundaries can be swapped out of order and rescanned.

It avoids a virtual DOM, hidden hydration pass, implicit startup fetch, component rerender loop, and browser snapshots that leak server-only cache contents.

Guide: docs/start/why-async.md · Contract: specs/framework/00-system-overview.md

The Abstraction Layers

Async uses L0-L7 abstraction layers. Layers describe the authoring surface; capabilities are protocol properties that land at the lowest layer the protocol allows.

LayerNameEra anchorAddsRequires
L0EnhancejQuery/Backbone; htmxServer-led HTML, native forms/actions, and behavior references on server-owned viewsScript tag
L1Interpretangular.js: runtime, no buildRuntime-interpreted app model: registries, components, lifecycleScript tag or ESM
L2BundleBuilt SPAsBuild as delivery, client routing, app serverBuild optional
L3SSRReact-without-JSX + SSR serverServer-rendered components with activation, no hydrationServer; build optional
L4TransformReact+JSXJSX/TSX transforms lowering to protocol recordsBuild
L5StreamStreaming SSR; SuspenseProgressive documents, boundary reveal orderingStreaming server
L6ReorderRSC; islands; Qwik-style server$Out-of-order settling, co-located server functions, chunks, and plans automated by the OptimizerOptimizer
L7OptimizeReact Compiler; TSRXWhole-program compilationSpec only today

Layers compose within one document: an L2-bundled SPA can host an L0-enhanced form next to an L5-streamed boundary. This package ships the no-compiler layers (L0-L3, L5) plus the first compiler-layer surfaces (./jsx, ./vite, ./runtime/*).

Guide: docs/start/layers.md · Contract: specs/framework/15-abstraction-layers.md

How It Works

The framework is a protocol stack: HTML attributes connect to registered state and behavior, server work returns explicit envelopes, route partials and stream patches replace named boundaries, and SSR activation resumes already-rendered HTML without hydration.

HTML Protocol

Loader scans regular HTML attributes. The shorthand prefixes are the author-facing syntax: async:, signal:, on:, class:, and intersect:.

AttributeBehavior
async:containerMarks a scannable app root
async:boundary="product"Marks a replaceable boundary
async:snapshotHolds serialized startup state
async:component="Card"Mounts a registered component
on:click="selectProduct"Delegated command event
on:submit="preventDefault; save"Sequential command chain
on:click="server.cart.add(productId)"Server command with signal args
on:intersect="trackSection"Continuous intersection lifecycle event
signal:text="product.title"Text binding
signal:value="productId"Form value binding with writeback
signal:attr:disabled="product.$loading"Attribute binding
class:selected="selected"Class toggle from a signal path

Guide: docs/runtime/html-protocol.md · Contract: specs/framework/04-dom-protocol.md

Signals & Async Signals

Signals are the state boundary for DOM bindings, handlers, server effects, router state, and async resources. Signal writes are synchronous; bindings, lifecycle callbacks, effects, and async refreshes are scheduled in deterministic phases.

constsignals=createSignalRegistry({productId: createSignal("sku-1")});constproduct=signals.asyncSignal("product",asyncfunction(){returnthis.server.products.get(this.signals.get("productId"));});

Guide: docs/runtime/signals.md · Contract: specs/framework/03-reactivity-system.md

App Hub & Registries

Async is the app hub singleton. It stores declarations, materializes fresh runtime registries on startup, exposes inspection APIs, and queues loader work that runs before a root attaches.

Async.use({signal: {count: createSignal(0)},handler: {increment(){this.signals.update("count",(count)=>count+1);}}});Async.start({root: document});

Guide: docs/runtime/app-hub.md · Contract: specs/framework/02-runtime-kernel.md

Components

Components are scoped fragment functions. They return strings or html templates; Loader inserts and scans the result, and scoped signals, handlers, effects, and lifecycle cleanup follow the fragment.

constToggle=component(functionToggle(){constselected=this.signal(false);returnhtml`<buttonon:click="${this.handler(()=>selected.update((value)=>!value))}"class:selected="${selected}"> Toggle</button> `;});

Guide: docs/runtime/components.md · Contract: specs/framework/05-component-system.md

Server Calls & Cache

Server registries run locally on the server. Browser proxies use an explicit transport supplied by the app; responses can return values, signal patches, browser cache patches, boundary HTML, redirects, or errors.

constserver=createServerProxy({endpoint: "/__async/server",
transport,
signals,
loader,
router
});awaitserver.cart.add("sku-1",2);

Guide: docs/runtime/server-calls.md · Contract: specs/framework/06-server-and-data-system.md

Router & Partials

The router lives behind @async/framework/router. It handles URL matching, route params, hash-based static-host routes, same-origin link and GET form interception, route partial swaps, and route-only router.* state.

Async.use({route: {"/products/:id": defineRoute("product.page")},partial: {"product.page"({ id }){returnhtml`<h1>Product ${id}</h1>`;}}});

Guide: docs/runtime/router-partials.md · Contract: specs/framework/07-routing-and-partials.md

SSR & Activation

SSR uses related app definitions: a server runtime renders HTML plus snapshots, and the browser runtime activates the existing document. Activation scans and attaches; it does not hydrate, diff, patch, rerender, or fetch route fragments.

constresponse=awaitcreateApp(serverApp,{target: "server",
request
}).render("/products/123");

Guide: docs/runtime/ssr-activation.md · Contract: specs/framework/08-resume-and-streaming.md

Streaming & Boundaries

Boundary swaps replace named regions and rescan inserted content by default. createBoundaryReceiver(...) adds per-boundary sequence tracking, signal/cache effects, and stale patch suppression for independently arriving patches.

awaitreceiver.apply({boundary: "product",seq: 1,signals: {product: {title: "Keyboard"}},html: `<h1 signal:text="product.title"></h1>`});

Guide: docs/runtime/streaming.md · Contract: specs/framework/08-resume-and-streaming.md

Install & Load

Install from npm:

pnpm add @async/framework

Load directly from a CDN for no-build prototypes:

<scripttype="module">import{Async,createSignal}from"https://unpkg.com/@async/framework@latest/browser.js";</script>

Use @async/framework/vite when a Vite app needs the Hono development server lane, a browser client build lane, or JSX optimizer reports.

Guide: docs/start/install.md · Build guide: docs/build/vite-hono.md

Examples

See examples/README.md for start commands and a short description of every example.

ExampleShows
examples/counterSignal text binding and delegated handlers
examples/productAsync signal loading, ready, and error boundaries
examples/componentsScoped fragment components and lifecycle hooks
examples/app-patternsFramework-native app composition, derived signals, and app-level errors
examples/streamingBoundary swaps with rescanned handlers
examples/server-callCommand events calling server functions
examples/hateoas-actionsHono-rendered HATEOAS links and forms enhanced into partial swaps
examples/routerCSR first render and local route boundary swaps
examples/partialsServer-rendered partial fragments
examples/cacheBrowser/server cache declarations
examples/ssrServer render output and browser activation snapshot
examples/vite-honoHono-backed Vite dev server plus client asset build
examples/vite-jsx-streamingJSX optimizer bootstrap with stream runtime slice selection
examples/sizeScenario-size fixtures for bundle and runtime slices

Async And htmx

Async and htmx are both HTML-first and avoid a virtual DOM, but they optimize for different boundaries. In layer model terms, htmx-style hypermedia is the L0 Enhance layer, and in Async it stays available at every layer above.

AreahtmxAsync
Primary modelHTML attributes issue HTTP requests and swap server responses.Server-generated HTML can stay primary; Async attributes add behavior, state, actions, and boundaries.
StateServer-owned hypermedia state; browser state is intentionally minimal.Server-led HTML can stay server-owned; browser signals are available when a view needs local state.
Server interactionDOM attributes describe HTTP verbs, targets, and swaps.Native method/action flows can submit to any backend; partial responses can return HTML or envelopes for boundary swaps.
RoutingUsually server navigation or htmx-boosted navigation.MPA and SSR keep navigation server-led; CSR, SPA, and signals modes opt into client-owned routing.
ComponentsServer-rendered HTML fragments.Scoped fragment functions today; the compiler layers add JSX/TSRX authoring.
Build storyNo build by default.Layers L0-L3 and L5 are no-build/CDN; the compiler layers (L4, L6, L7) add build or compiler steps.

Async supports server-led views: any backend can render HTML strings for full documents or fragments, native forms can post through ordinary method and action attributes, and browser navigation can stay native in MPA/SSR modes. Add Async attributes only where a fragment needs local signals, command handlers, server functions, boundary swaps, or streamed patches. Use htmx when its HTTP-attribute model is the desired contract. Use Async when server-led HTML should share a protocol with local signals, registered browser/server handlers, route partials, streaming boundaries, and the compiler layers. See examples/hateoas-actions for a Hono-rendered HATEOAS flow using links, forms, verbs, and partial swaps.

Guide: docs/start/why-async.md · Contract: specs/framework/15-abstraction-layers.md

Status

The core runtime is intentionally small. Build-required JSX (L4) has optimizer artifacts for event, signal, stream, and children-fragment lowering, while full compiler emission, lazy chunk manifests, TSRX lowering, server resource compilation, and higher-level resumability metadata remain compiler-layer work (L6 and L7).

Contracts: specs/framework/12-composition-patterns.md · specs/framework/15-abstraction-layers.md · specs/framework/16-whole-program-compiler.md

Documentation Map

PageQuestion it answers
Getting StartedWhat is the smallest running app?
App Authoring ContractWhich framework-native patterns should applications and examples follow?
Install & LoadHow do npm, CDN, UMD, and import-map loading work?
Why AsyncWhat does Async keep and avoid?
Core ConceptsWhich runtime pieces make up an app?
LayersHow do L0-L7 fit together?
Runtime OverviewWhat happens when a root starts?
App Hub & RegistriesHow are declarations registered, inspected, and materialized?
HTML ProtocolWhich attributes connect HTML to runtime behavior?
Signals & Async SignalsHow does state update and async work refresh?
ComponentsHow do scoped fragments, children, lifecycle, and intersection work?
Router & PartialsHow do routes, partials, modes, and boundaries work?
Server Calls & CacheHow do server functions, envelopes, and cache split work?
SSR & ActivationHow does server-rendered HTML start in the browser?
Streaming & BoundariesHow do swaps, refresh plans, morphing, and patch ordering work?
Build ProfileWhat does the compiler-layer profile promise?
Vite & HonoHow does the Vite plugin wire a Hono dev server and client build?
EntrypointsWhich package subpaths expose which surfaces?
Errors & DiagnosticsWhich stable codes, hints, callbacks, and events report failures?
ExamplesWhich runnable example demonstrates each surface?

Contributing & Release

Common checks:

pnpm run pipeline:pages
pnpm run registry:lint
git diff --check

Pipeline and release automation are generated from pipeline.ts; update that source and run the sync checks before changing generated workflow output.

Guide: CONTRIBUTING.md

About

No-build web framework runtime with signals, command events, server calls, route partials, cache split, SSR activation, and streaming boundaries.

Topics

Resources

Contributing

Stars

4 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages