Programming Model

The Vango execution model in practice: Setup, render purity, Resources, Actions, and the session loop.

Updated 2026-05-09Edit this page

Programming Model

Vango is opinionated about where code belongs. If you follow the model, the framework stays predictable under refactors and deploys. If you fight it, you end up recreating the same ownership bugs Vango was designed to prevent.

The Core Rules

  • The server owns authoritative UI state.

  • The session loop is the single reactive writer.

  • vango.Setup allocates state and lifecycle.

  • Render closures stay pure.

  • Blocking reads belong in setup.Resource or setup.ResourceKeyed .

  • Blocking mutations belong in setup.Action or a page Action .

  • Dynamic lists use RangeKeyed with stable identity.

  • DOM ownership stays explicit: server-owned by default, hook/island/WASM only when chosen deliberately.

Setup Allocates, Render Computes

go
1func TodoList(p TodoListProps) vango.Component {
2    return vango.Setup(p, func(s vango.SetupCtx[TodoListProps]) vango.RenderFn {
3        items := setup.Signal(&s, []Todo{})
4        newText := setup.Signal(&s, "")
5
6        return func() *vango.VNode {
7            return Div(
8                Input(Value(newText.Get()), OnInput(newText.Set)),
9                RangeKeyed(items.Get(), func(item Todo) string { return item.ID }, renderTodo),
10            )
11        }
12    })
13}

This separation is the center of the framework:

  • Setup runs once per mount

  • render runs repeatedly

  • Setup can allocate

  • render must not allocate or do blocking work

Async Work Has A Home

async readsetup.Resource / setup.ResourceKeyed
async live mutationsetup.Action
progressive form POSTpage Action + setup.RouteForm
shareable URL statesetup.URLParam
browser behavior on server-owned DOMHook(...)
client-owned subtreeJSIsland(...)
client-local Go executionWASMComponent(...)

Stop sign

If you feel pressure to do blocking I/O in render, Setup, event handlers, `OnMount`, `Effect`, or `OnChange`, you are on the wrong side of the Vango contract.

  • Use Link(...) and NavLink(...) for normal navigation.

  • Use ctx.Navigate(...) from event handlers or post-commit lifecycle code.

  • Do not call ctx.Navigate(...) from a page handler or render closure.

If external callbacks need to write reactive state, dispatch back onto the session loop with ctx.Dispatch(...) instead of inventing another concurrency path.

Identity Matters

Dynamic lists should use RangeKeyed with a stable key. Keyed identity is not optional bookkeeping in Vango; it is part of patch correctness.

Read Further

  • Components & State

  • Routing & User Flows

  • Client Boundaries