How It Works
The whole framework in one sentence: you write HTML, the server does the work, the browser renders what the server sent. This page walks through what actually happens — so you know where your code runs before you write it.
The Mental Model
There is exactly one interaction model in What:
- The browser sends a request — a link click, a form submit, a
w-getbutton, or a WebSocket message. - The server renders HTML.
- The client puts that HTML into the page.
Every feature reduces to this. There is no virtual DOM, no hydration, no client-side state machine, no build step. If you have written HTML and can read a template variable like #user.name#, you already understand the architecture.
What Happens When a Page Loads
You request /about. On the server:
- The file is the route. The server finds
site/about.html. No router configuration exists anywhere. - Directives are read. The
<what>block at the top of the file (and anyapplication.whatin the directory chain) sets the title, layout, auth rules, and data to fetch. - The template is processed — in a fixed order: includes, then loops, then conditionals, then components, then variables.
#user.name#becomes the actual name;<if>blocks keep or drop their content. - The layout wraps the page. Your content lands where the layout's
<slot/>is. - Finished HTML is sent. The browser renders a complete page. View-source shows real content, not a loading shell — which is also why search engines see everything.
What Happens When You Click a Link
Internal links are “boosted” by default: instead of a full page reload, the tiny client runtime fetches the next page's HTML from the server and swaps it in, keeping scroll and styles smooth. It is still the same server-rendered HTML — just delivered without a white flash. Add w-boost="false" to any link to opt out and get a normal full navigation.
What Happens When You Click a w-post Button
<p>Count: #session.count|default:"0"#</p>
<button w-set="session.count += 1">+1</button>
When the button is clicked:
- The client sends the mutation to the server. Your “code” runs on the server — the browser only dispatched a request.
- The server updates
session.count(state lives server-side, keyed to the visitor's session cookie). - The server responds, and every element bound to
#session.count#on the page updates. No page reload.
w-get and w-post work the same way for fetching content: the server renders an HTML fragment, and the client swaps it into whatever w-target points at.
Where Your Data Lives
| Scope | Lives | Visible to | Typical use |
|---|---|---|---|
session.* | Server | One visitor | Counters, carts, form progress |
application.* | Server | All visitors | Site-wide settings, shared data |
wired.* | Server | All connected visitors, live | Dashboards, presence, chat |
The browser never holds application state. Refresh the page, open a second tab, switch devices with the same session — the server is the single source of truth.
Live Updates (Wired State)
wired.* variables use the same model over a different transport: when a wired value changes on the server, the server pushes the update over a WebSocket and every connected page's w-bind elements refresh. Same primitive — the server sends content, the client puts it in the DOM.
Do I Need a Server in Production?
- Dynamic sites (sessions, forms, auth, live data): yes — the same binary that runs
run-what devserves production. Deploy it withrun-what deploy(SSH, Docker) orrun-what bundlefor a single self-contained executable. - Static sites (content only, no per-visitor state): no —
run-what buildpre-renders every page to plain HTML you can host anywhere.
So What JavaScript Ships?
One small runtime (what.js, ~16 KB gzipped), injected automatically. It does five things: dispatch requests, swap HTML into the page, listen for wired updates, run server-authored form validation, and progressively enhance forms. You never write JavaScript against it — and for most apps, you never write JavaScript at all.