- Components define what the user sees
- State holds the data
- Expressions read state to keep the display current
- Actions respond to interactions by changing state or calling your server
Components
Components are the building blocks of every interface.Text renders content. A Button triggers actions. A Card wraps other components in a visual container. Components form a tree where parents contain children, and in Python, the with statement expresses that nesting naturally:
Row arranges children horizontally, Column stacks them vertically, Grid places them in a grid, and they all nest freely. Switch to the Protocol tab to see the JSON that this Python produces; that’s what the renderer actually receives. The Composition guide covers layout primitives, cards, grids, loops, and forward references.
State
State is a key-value store that the renderer maintains for the lifetime of your app. It starts with whatever values you provide and changes as users interact. State is global: every component can read from it and every action can write to it. Interactive components are the most natural source of state. AnInput(name="city") writes its current text to the city key on every keystroke. A Slider(name="volume") writes its position on every drag. Components with a value prop also seed their state key automatically: Input(value="London") means the key starts with "London" before the user types anything.
The State guide covers seeding, form bindings, dot paths for nested data, and how state gets written.
Expressions
Expressions are how components read from state and compute display values.{{ count }} in a text component means “display whatever count holds right now, and update whenever it changes.” Every {{ }} expression declares a live dependency: the renderer tracks which keys it reads, re-evaluates when any change, and updates just those parts of the display. This happens entirely in the browser, instantly.
Beyond reading, expressions can compute: {{ price * quantity }} multiplies two keys, {{ status == 'active' }} produces a boolean, {{ revenue | currency }} formats a number as $2,847,500.00. The language is intentionally bounded, covering enough to drive any UI while keeping business logic in your Python code.
In Python, you write expressions using the Rx class. Rx("count") compiles to {{ count }}, and operators chain naturally: (price * quantity).currency() compiles to {{ price * quantity | currency }}. Stateful components have a .rx shorthand; slider.rx returns an Rx bound to the slider’s state key, so you can wire components together without writing key names by hand.
The Expressions guide covers the Rx DSL, operators, and formatting. The Expression Reference is the lookup page for every operator, pipe, and special variable.
Actions
Actions are what happen when users interact. Click a button, change a slider, submit a form: each interaction can carry an action that fires when the event occurs. Client actions run instantly in the browser:SetState assigns a value, ToggleState flips a boolean, AppendState pushes onto an array, ShowToast gives feedback. Server actions cross a network boundary: CallTool invokes an MCP tool, Fetch makes an HTTP request. They’re asynchronous, they can fail, and the renderer handles both outcomes through on_success and on_error callbacks.
Most real interactions use both. A “Save” button sets a loading flag (instant), calls the server (async), then shows success or failure (instant, based on the outcome). Actions can be sequenced as lists and chained through callbacks.
The Actions guide covers both families, chaining, the $event variable, and common patterns like loading state and optimistic updates.
How it runs
These four concepts are split across two environments. Build time is when your Python code runs. You create components, nest them into a tree, attach state references and actions, and when you’re done, Prefab serializes everything to a JSON document. Your server’s job ends there. Render time is everything after. A JavaScript renderer receives that document and turns it into a live interface: a React application running in the browser or inside an MCP host like Claude Desktop. The renderer manages state, evaluates expressions, handles interactions, and updates the display. None of that involves your server unless aCallTool or Fetch action explicitly calls back.
The cycle runs continuously:
- Python builds a component tree and the renderer displays it
- A user interacts: clicks, types, drags
- An action fires: state updates directly, or a request goes out to the server
- State changes trigger expression re-evaluation; the display updates
- For server actions: the tool returns new content or state, and the renderer merges it