Components and Setup
The canonical stateful component shape in Vango is vango.Setup.
Use vango.Setup for stateful components
1type CounterProps struct {
2 Initial int
3 Children vango.Slot
4}
5
6func Counter(p CounterProps) vango.Component {
7 return vango.Setup(p, func(s vango.SetupCtx[CounterProps]) vango.RenderFn {
8 props := s.Props()
9 count := setup.Signal(&s, props.Peek().Initial)
10
11 return func() *vango.VNode {
12 p := props.Get()
13 return Div(
14 Button(OnClick(count.Dec), Text("-")),
15 Span(Textf("%d", count.Get())),
16 Button(OnClick(count.Inc), Text("+")),
17 p.Children,
18 )
19 }
20 })
21}
Rules:
use a named props struct
use vango.NoProps when there are no props
Setup returns exactly one render closure
stateful component functions should normally return vango.Component
Props
Inside Setup, use props := s.Props().
props.Get() is a tracked read for render
props.Peek() is an untracked read for initialization
Use Get() in render and Peek() for one-time setup decisions like initial signal values.
Props are runtime-only. They do not persist, and props updates do not rerun Setup.
Slots
Children are explicit.
1type CardProps struct {
2 Title string
3 Children vango.Slot
4}
Render slots directly with p.Children. For multiple slots, add more vango.Slot fields to the props struct.
Do not invent ad hoc []any child APIs for stateful components.
Stateless helpers are encouraged
Not every reusable view needs to be a component.
1func Badge(label string) *vango.VNode {
2 return Span(
3 Class("inline-flex rounded px-2 py-1 text-xs"),
4 Text(label),
5 )
6}
Use a plain function when there is no reactive allocation and the helper is just deterministic view composition.
Allocation rules
Reactive allocation in Setup must be unconditional.
Bad:
1if props.Peek().Admin {
2 _ = setup.Signal(&s, true)
3}
Bad:
1return func() *vango.VNode {
2 _ = setup.Signal(&s, 0)
3 return Div()
4}
Good:
1adminFlag := setup.Signal(&s, false)
2
3return func() *vango.VNode {
4 if !props.Get().Admin {
5 return Div()
6 }
7 return Div(Textf("%t", adminFlag.Get()))
8}
The rule is: allocate first, branch later.
Lifecycle methods
SetupCtx gives you:
s.Ctx()
s.OnMount(...)
s.Effect(...)
s.OnChange(...)
s.OnPersistError(...)
OnMount
Use OnMount for one-time post-commit setup such as subscriptions or small integrations.
Do not use it for blocking I/O, long-running goroutines, or signal allocation.
Effect
Use Effect for bounded reactive side effects after commit.
Keep effects cheap and non-blocking. Prefer framework helpers like Timeout, Interval, Subscribe, or GoLatest instead of raw goroutines.
OnChange
Use OnChange when one value changing should synchronously orchestrate another state transition.
Common uses:
reset dependent state
clear local errors on key changes
coordinate post-success UI cleanup
The standard checklist
use vango.Setup
allocate all state in Setup
keep render pure
no blocking I/O in Setup or render
no manual goroutines in Setup, render, or lifecycle callbacks
The next layer is how state and async work flow through a component. Read State and Async Work next.