{}

Incremental updates

Every BESPA page carries a small bag of key-value pairs called state. When a state value changes, the framework re-renders only the widgets that asked to be notified, sends those HTML fragments back to the browser, and the client swaps them into the DOM. No JS diffing, no full reload, no flash.

A live state viewer

The grouping frame below displays the current value of the state variable named x. It opts in to redraws on changes to x by calling RedrawIfChanged:

State variable "x" viewer
x = ""

State variables move when you click a link whose href starts with ?. Try it:

?x=foo?x=bar?x= (clear)

What happened on each click: the browser posted the state delta back to the server over a small fetch request, the server re-rendered only the widgets opted in for x (just the viewer above), and the client swapped that single fragment into the DOM. The rest of the page — including the AppBar, headings, and this paragraph — stayed exactly as it was.

Show, hide, and redraw

A widget can also choose to disappear entirely based on state. The secret widget below combines HideIfNotEq (visibility) with RedrawIfChanged (re-render trigger) — both keyed on the variable secret:

?secret=show?secret=hide

When HideIf hides a widget, the framework still emits an empty placeholder element in its place — so a later state change can swap real content back in without breaking the DOM shape. The hide isn’t display:none CSS; it’s the widget disappearing entirely.

Compared to a full reload

For contrast, the following link uses a regular URL — no ? prefix — which triggers a full page reload with x set in the initial query string:

incremental?x=baz

Both routes get you to the same end state — x = baz — but the full reload re-renders every widget on the page and the browser repaints the whole document. The incremental path touches only the viewer.

The wire format

Underneath, partial redraws are a small protocol you can inspect in the browser’s network panel — or hit with curl for debugging.

Request. The client posts to the page’s URL with the header Bespa-Fetch: 1 and a form body that contains:

Example body: name=Ada&theme=dark&_changed=name. The server uses _changed to decide which widgets to re-render; RedrawIfChanged(r, "name") is true when "name" appears in the _changed list.

Response. Plain HTML — a series of fragments concatenated. Each fragment is the root element of one redrawn widget, carrying a data-id attribute that matches the element it replaces in the live DOM. A typical response body:

<span data-id="abc" class="GroupingFrame">…new contents…</span>
<button data-id="def" class="ButtonFilled">…</button>

The client walks the response, matches each data-id against the current DOM, and swaps. Anything not in the response stays put.

Why Bespa-Fetch: 1. The header tells the server this is a partial-redraw request, not a full page load. Redirect responses also use a different envelope when the header is set — the server writes Location: … as a plain-text response body instead of an HTTP 3xx, so the client can follow it without the browser losing the in-flight fetch context.

Live updates and push

The protocol above is one-way: the client initiates, the server responds. BESPA does not maintain persistent connections — there’s no built-in websocket or SSE layer, and no managed subscription engine.

That’s deliberate. Persistent connections require the server to track who’s connected, what they’re subscribed to, and how to fan messages out — state that lives across requests. BESPA is stateless throughout, which is what keeps it small and horizontally trivial to scale.

You have two options when you need live-ish behavior:

Watch it happen

The debugger pinned to the bottom-right corner shows the current state, the URLs that have been fetched, and the HTML fragments that came back. Open it and repeat the clicks above to see the protocol live.

See also

The action-URL pattern — the full grammar of link / form URLs, of which ? is one prefix.

Build → When to redraw — picking which widgets opt in to which state variables.

Nesting pages — how the same protocol drives modals, side panels, and embedded sub-pages.