Introduction

AFW compiles JSX directly into imperative native DOM operations. There is no virtual DOM tree. Reusable components compile to Custom Elements; route pages and layouts compile to factories. State changes run the dependent DOM bindings.

Why AFW

  • No virtual DOM — the compiler emits exactly the DOM calls a view requires and binds each update to the node it affects. Keyed lists still reconcile their items when the list changes.

  • Standards-based components — every component is a real HTMLElement, usable in any page, with or without a framework.

  • Fine-grained reactivity — the $state, $derived, and $effect macros wire bindings at compile time. Signals track dependencies when those bindings run, so component code reads and writes state without .value.

  • Client and server building blocks — file-based routing and layouts, middleware and API routes, route loaders and cookies, client-side route guards, plus static generation, server rendering, and hydration. Server features require an app-owned server entry; a browser build alone does not start one.

A first component

JSX
export default function Counter() {
  let count = $state(0);

  return <button onclick={() => count++}>Clicked {count} times</button>;
}

count is a signal. Reassigning it updates only the text node it feeds; nothing else re-runs.

Output

The button above is that snippet, compiled and running on this page — click it.

No virtual tree to reconcile

Compiled output constructs the DOM directly and is interactive immediately. Under server rendering or static generation, first paint adopts the server-rendered nodes in place rather than re-rendering them — see Hydration.

Start with Installation, then learn the Project Structure. The Components and Reactivity guides explain how to build your UI.

Last updated on
Edit this page