Mental Model

Understand the session loop, where code is allowed to run, and the correctness boundaries that define Vango apps.

#model#session-loop#architecture

Mental Model

Vango is a server-driven UI framework with a strict ownership model. Most mistakes in Vango come from importing habits from client-heavy web frameworks.

Server-driven UI

In Vango:

  • the server owns state

  • the server renders UI

  • the browser sends events

  • the browser applies patches

The browser is not the source of truth.

That means UI is a projection of server state, event handlers mutate server state, and rerendering happens on the server.

The session loop is the single writer

Each connected browser tab has a session. Each session has a single-writer event loop.

That gives you:

  • serialized signal writes

  • deterministic rerenders

  • fewer accidental write races

The most important rule in Vango is simple: all reactive writes must happen on the session loop.

If code runs off-loop, bring it back through a framework boundary such as setup.Resource, setup.Action, a page Action, or ctx.Dispatch(...) for external integrations.

Where code runs

SetupNoYesNo
renderNoNoNo
event handlerNoNoYes
Resource loaderYesNoNo
setup.Action work / page ActionYesNoNo
hook / island / WASM codebrowser-local onlybrowser-local onlybrowser-local only

Use that table literally.

Initial load and reconnect

Typical flow:

  1. browser requests a page

  2. server matches the route and renders HTML

  3. browser shows content

  4. runtime script connects to the live endpoint

  5. browser events go over the live transport

  6. server rerenders and sends patches

Reconnect behavior is explicit. Successful resume may use a full resync to realign browser DOM with the rebuilt server tree. If SSR and WebSocket route agreement drift apart, you can get patch mismatches, reconnect loops, or state that seems attached to the wrong page.

Purity rules

Setup:

  • allocates state

  • registers lifecycle

  • stays non-blocking

Render:

  • computes UI from current state

  • stays deterministic

  • does not allocate

  • does not write state

Resources and Actions:

  • own blocking work

  • run off-loop

  • return results through the framework

Effects:

  • run after commit

  • react to dependency changes

  • stay bounded and non-blocking

Performance model

The expensive place is the session loop. Keep render closures, event handlers, and synchronous effect bodies cheap.

Move expensive work to:

  • Resource loaders

  • setup.Action work functions

  • page Action handlers

Also remember:

  • unstable list keys create large patches

  • uncontrolled client DOM mutation creates patch mismatches

  • too much persisted state hurts resume time

Stop signs

If you hit any of these, stop and fix the contract violation instead of papering it over:

  • repeated self-heal reloads

  • navigation that only works after refresh

  • list bugs after reorder or filtering

  • pressure to mutate server-owned DOM from the client

  • desire to jump to low-level packages because the first implementation is fighting the framework

Forbidden fixes

Do not:

  • add location.reload to mask patch mismatches

  • call ctx.Navigate(...) in a page handler or render closure

  • use unstable list keys

  • repair server-owned DOM with inline scripts after boot

  • write signals from Resource loaders or Action workers

  • reach for low-level pkg/* packages for ordinary app code

  • Components and Setup for stateful component structure

  • State and Async Work for signals, resources, and actions

  • Client Boundaries for hooks, islands, and WASM