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$effectmacros 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
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.
The button above is that snippet, compiled and running on this page — click it.
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.