Components & State

Signals, memos, Resources, Actions, list identity, and persisted state surfaces in Vango.

Updated 2026-05-09Edit this page

Components & State

The Vango Developer Guide is exhaustive, but the launch version reduces the state model to the decisions you make every day while building app code.

Local Reactive State

Use setup.Signal for local state owned by one component.

go
1count := setup.Signal(&s, 0)
2search := setup.Signal(&s, "")

Use setup.Memo for derived values that should recompute from other reactive inputs.

Allocate both signals and memos unconditionally inside vango.Setup. Do not allocate reactive primitives inside render.

Async Reads

go
1user := setup.ResourceKeyed(&s,
2    func() int { return props.Get().UserID },
3    func(ctx context.Context, id int) (*User, error) {
4        return repo.GetUser(ctx, id)
5    },
6)

Use Resources when:

  • data can load asynchronously

  • the request is a read

  • render should stay pure

Async Mutations

go
1saveProfile := setup.Action(&s,
2    func(ctx context.Context, in SaveProfileInput) (*Profile, error) {
3        return repo.SaveProfile(ctx, in)
4    },
5)

Use Actions when:

  • the change originates from live UI

  • the work may block

  • you want a structured pending/error/result surface

Persisted State Surfaces

vango.SessionKey[T]Small session-owned durable values
setup.SharedSignalSession-shared persisted state
setup.GlobalSignalApp-global persisted state

Persist only deliberate UX state. Do not use persisted primitives as a default replacement for local reactive state.

SessionKey declarations should be package-scoped and typed:

go
1type ThemePrefs struct {
2    Mode string
3}
4
5func (ThemePrefs) VangoSchemaID() string {
6    return "myapp:ThemePrefs:v1"
7}
8
9var ThemeKey = vango.NewSessionKey[ThemePrefs](
10    "theme",
11    vango.Default(ThemePrefs{Mode: "system"}),
12)

Persisted initializers must be deterministic. Use literals, constants, and simple composite values; do not derive persisted initial values from props, time, environment, random IDs, I/O, or helper calls.

List Identity

Use RangeKeyed for dynamic collections:

go
1RangeKeyed(items.Get(),
2    func(item Todo) string { return item.ID },
3    func(item Todo) *vango.VNode {
4        return Li(Text(item.Text))
5    },
6)

The Short Checklist

  • Allocate all reactive primitives in Setup.

  • Never allocate reactive primitives in render.

  • Keep writes on the session loop.

  • Use keyed lists whenever order or membership can change.

  • Persist only what must survive navigation, reconnect, or deploy.

  • Add a stable VangoSchemaID() when a persisted type should survive renames or package moves.