useState, no callbacks threading data up through a tree. Components declare which keys they depend on, and changes propagate everywhere automatically. Any component anywhere in the tree can read or write any state key.
Providing initial values
State starts empty unless you seed it. For apps running viaprefab serve or as a standalone Python script, use set_initial_state(). It returns a proxy object whose attributes are reactive references to the keys you just defined — so you can immediately start using them in components without a separate declaration step:
PrefabApp, pass state to the constructor:
value prop seed their state key too. Input(name="city", value="London") registers city with an initial value of "London", so no separate set_initial_state call is needed.
Either way, those keys are immediately available to every expression in your component tree.
Interactive components as state sources
Interactive components with aname prop are the most natural state source. They automatically sync their current value to that key on every interaction.
As you type in the input, the greeting updates on every keystroke. This is a gentle introduction to Prefab’s expression language: double curly braces reference state values, and the pipe operator provides a fallback ("stranger") when the key is undefined.
Every interactive control works this way. A Slider(name="volume") writes its position on every drag. A Checkbox(name="agree") writes true/false. A Select(name="size") writes the selected option value. Whatever name you give becomes a key that expressions and actions can reference.
Reading state
Inside component props,{{ key }} template expressions resolve to the current value at render time. Any string prop accepts these expressions, and the renderer re-evaluates them whenever a referenced key changes. Text("{{ count }}") displays the number; Progress(value="{{ volume }}") drives the bar.
The expression language itself supports operators and formatting: {{ count + 10 }} adds ten, {{ name | upper }} uppercases. You can write these directly in any string prop.
In Python, Rx objects compile to the same {{ key }} expressions in the protocol output, but in your code they behave like Python values — you can apply operators, use them in f-strings, and combine them with other values. The most common way to get an Rx is through the return value of set_initial_state, which is a proxy whose attributes are reactive references to the keys you defined:
STATE constant works identically but doesn’t require capturing the return value — useful when state is set by actions rather than initialization, or when you want a quick reference without calling set_initial_state first:
let bindings or forward references), use Rx directly: Rx("key"). The full Rx system, including operators, formatting, and the .rx shorthand, is covered in Expressions.
Nested state and dot paths
State values can be nested objects, and you reach into them with dot notation.{{ profile.name }} reads the name field of the profile object. The name prop accepts dot paths too, so inputs can bind directly to nested fields:
Integer segments address array items. {{ todos.0.done }} reads the done field on the first item in todos. Inside a ForEach loop, combine this with {{ $index }} to target whichever row the user is interacting with: SetState("todos.{{ $index }}.done", True) checks off the current item.
If any segment along a path is missing or the wrong type, the expression resolves to undefined rather than throwing. Reads return undefined gracefully; writes are no-ops with a console warning.
Writing to state
Two things write to state: interactive controls (automatically, via thename prop) and actions (explicitly, in response to events). SetState assigns a value. ToggleState flips a boolean. AppendState and PopState manipulate arrays. CallTool and Fetch can write their results into state via result_key.
State is deliberately simple: a flat map with dot-path addressing for nesting. There are no computed properties, no watchers, no derived state in the store itself. Derived values belong in expressions, where they’re computed at render time from whatever state holds. Complex computations belong in Python, run before you return the component tree, or in a server action.