Managing Reactive UI State in Reflex with Computed Variables
Learn how to use Reflex State and @rx.var to build reactive UIs in pure Python, including the critical differences between base and computed variables.
17 Sept 2026, 06:47 UTC

The Core Problem: Synchronizing Server State with Client UI
In traditional full-stack development, you must manually synchronize a backend database or API with a frontend state manager like Redux or Vuex. Reflex eliminates this by hosting the application state on the server and automatically pushing updates to the browser via WebSockets. The challenge for developers is knowing when to use a standard state variable versus a computed variable to ensure the UI remains performant and the logic stays DRY (Don't Repeat Yourself).
The Mechanism: Base Vars vs. Computed Vars
Reflex uses a State class to track the current condition of the application. Within this class, there are two primary ways to handle data:
- Base Vars: These are class attributes with type annotations. They are serialized to the client and stored in the server-side session. Any change to a base var inside an event handler triggers a re-render of all UI components that reference it.
- Computed Vars: Defined using the
@rx.vardecorator, these are read-only properties derived from base vars. They are cached on the server and only re-calculate when their dependent base vars change, preventing expensive logic from running on every single render cycle.
Worked Example: A Dynamic Shopping Cart
The following implementation demonstrates how to use a base var for the item count and a computed var to calculate the total price automatically.
import reflex as rx
class CartState(rx.State):
# Base Var: Serialized and stored per user session
item_count: int = 0
unit_price: float = 19.99
# Computed Var: Only re-calculates when item_count or unit_price changes
@rx.var
def total_price(self) -> str:
total = self.item_count * self.unit_price
return f"${total:.2f}"
def add_item(self):
self.item_count += 1
def remove_item(self):
if self.item_count > 0:
self.item_count -= 1
def index():
return rx.vstack(
rx.heading("Shopping Cart"),
rx.text(f"Items: {CartState.item_count}"),
rx.text(f"Total: {CartState.total_price}"),
rx.hstack(
rx.button("Add", on_click=CartState.add_item),
rx.button("Remove", on_click=CartState.remove_item),
),
)
app = rx.App()
app.add_page(index)
Operational Requirements and Execution
To run this code, you need a Python environment with Reflex installed. Run these commands in your terminal:
pip install reflex(Install the framework)reflex init(Initialize the project structure)reflex run(Compile the frontend and start the server)
Permissions: The user running the command must have permissions to create directories and bind to the default ports (usually 3000 for the frontend and 8000 for the backend).
Limitations and Common Pitfalls
1. Serialization Constraints
Base vars must be JSON-serializable. If you attempt to store a complex Python object (like a database connection or a custom class instance) directly in a State attribute, the application will likely fail during serialization to the client. Use IDs or primary keys instead of full objects.
2. The Mutation Trap
Reflex tracks changes to state variables to determine when to update the UI. When using lists or dictionaries, mutating them in place (e.g., self.my_list.append(item)) may not always trigger a UI refresh. The safest pattern is to reassign the variable:
# Avoid this:
self.items.append(new_item)
# Do this:
self.items = self.items + [new_item]
3. Network Latency
Because every event handler executes on the server, there is a round-trip delay over the WebSocket. This makes Reflex unsuitable for high-frequency interactions such as:
- Real-time canvas drawing
- Complex drag-and-drop animations
- Character-by-character input validation (unless debounced)
4. Session Isolation
By default, rx.State is per-user. If you define a variable in the state class, User A cannot see User B's values. To share data across users, you must integrate an external database or a Redis cache rather than relying on class-level attributes.
Verification and Diagnostics
To verify that your computed variables are working efficiently, add a print("Computing total...") statement inside the @rx.var method. When you interact with the UI, you should see the print statement trigger only when the dependent base var changes, not on every page interaction.
To check for session isolation, open the application in two different browser windows. Increment the counter in one; the other should remain unchanged, confirming that the state is correctly scoped to the individual session.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.