In Lesson 2 you used rx.cond instead of if, and in Lesson 3 you used rx.foreach instead of for. It's Python, so why can't you just use regular Python syntax? Once you understand the answer, most of the errors you'll run into in Reflex suddenly make sense.
The code in a Reflex app runs at two different moments.
| Compile time | Runtime | |
|---|---|---|
| When | Once, when the app starts | Continuously, while the app is running |
| What | UI functions (show_login, render_item …) | Event handlers (increment, toggle_login …) |
| Where | Python code is turned into JavaScript (React) code | Python on the server |
| State values | Not known yet | The real values are there |
When the app starts, UI functions are translated into JavaScript and sent to the browser. At translation time, nobody knows which user will click what yet, so LoginState.logged_in inside a UI function isn't a real True/False. It's a placeholder that "gets filled in later, in the browser." That placeholder is exactly what a Var is.
Event handlers, on the other hand, aren't translated. They run as plain Python on the server, so inside a handler you can use any Python code or library you like.
The handler below uses if and even calls a function defined outside the State. It runs on the server at runtime, so that's no problem.
def check_even(num: int):
return num % 2 == 0
class MyState3(rx.State):
count: int = 0
text: str = "even"
@rx.event
def increment(self):
# Use any Python code within state.
# Even reference functions defined outside the state.
if check_even(self.count):
self.text = "even"
else:
self.text = "odd"
self.count += 1
def count_and_check():
return rx.box(
rx.heading(MyState3.text, as_="h2"),
rx.button("Increment", on_click=MyState3.increment),
)localhost:3000
Try itYou can use Python inside UI functions too, but only on values already known at compile time (anything that doesn't use a state var). Below, range(5) and check_even(i) are fully evaluated when the app starts.
def show_numbers():
return rx.vstack(*[rx.text(f"{i} is {'even' if check_even(i) else 'odd'}") for i in range(5)])localhost:3000
Live preview0 is even
1 is odd
2 is even
3 is odd
4 is even
Because state var values aren't known at compile time, each of the following raises an error inside a UI function.
Checking a Var's value with if: whether it's true or false isn't known yet. The same goes for choosing a prop value.
class BadState(rx.State):
count: int = 0
def count_if_even():
return rx.box(
rx.heading("Count: ", as_="h2"),
# This will raise a compile error, as BadState.count is a var and not known at compile time.
rx.text(BadState.count if BadState.count % 2 == 0 else "Odd"),
# Using an if statement with a var as a prop will NOT work either.
rx.text("hello", color="red" if BadState.count % 2 == 0 else "blue"),
)Looping over a list Var with for: how many items it holds isn't known yet.
class BadState(rx.State):
items: list[str] = ["Apple", "Banana", "Cherry"]
def loop_over_list():
return rx.box(
# This will raise a compile error, as BadState.items is a list and not known at compile time.
*[rx.text(item) for item in BadState.items]
)Passing a Var to a Python function like len(): there's no string to count characters in yet.
class BadTextState(rx.State):
text: str = "Hello world"
def format_text():
return rx.box(
# Python operations such as `len` will not work on state vars.
rx.text(len(BadTextState.text)),
)So how do you show "Even if it's even, Odd if it's odd" on the page? Reflex provides Var operations that work on Vars. When you use operators like %, == or > on a Var, the result is also a Var, and the actual calculation happens in the browser once the value arrives.
class CountEvenState(rx.State):
count: int = 0
@rx.event
def increment(self):
self.count += 1
def count_if_even():
return rx.box(
rx.heading("Count: ", CountEvenState.count, as_="h2"),
rx.cond(
# Here we use the `%` and `==` var operations to check if the count is even.
CountEvenState.count % 2 == 0,
rx.text("Even"),
rx.text("Odd"),
),
rx.button("Increment", on_click=CountEvenState.increment),
)localhost:3000
Try itEven
Now one table sums up why you needed the tools from Lessons 1 to 3.
| What you want | Doesn't work in UI code | Use this instead |
|---|---|---|
| Different content depending on a condition | if / else | rx.cond(condition, if_true, if_false) |
| Looping over a list | for, list comprehensions | rx.foreach(items, render_fn) |
| Getting a length | len(x) | x.length() |
| Math and comparisons | Passing Vars to Python functions | Var operations like %, ==, > |
| Complex calculations | Python inside a UI function | Compute in an event handler and store it in State |
Good to know — When in doubt, ask yourself: "When does this code run?" Inside a UI function, it's compile time, so a Var is still an empty placeholder. Inside a handler, it's runtime, so you have the real value.
format_text from 4-3 so it shows the number of characters. Hint: BadTextState.text.length().count_if_even, show "Big!" when count is 10 or more. Hint: rx.cond(CountEvenState.count >= 10, ...).MyState3, with using Var operations, as in CountEvenState. Which one needs fewer State variables?if, for or len() on state vars in UI code.rx.cond and rx.foreach.← Previous
Lesson 3 · Drawing lists: rx.foreach
Next →
Lesson 5 · Making it look good: components, props and style
Get news about new lessons
We'll email you when this course gets new lessons or a follow-up course opens.
R
ReflexKR
Learn Python and Reflex by doing. Every example was run and verified on Reflex 0.10.0.
ReflexKR is free, with no banner ads. If it helped you, you can support it with a coffee. ☕ Buy us a coffee
Affiliate disclosure© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Questions & answers
Ask anything about this lesson. Questions from every language version of this page appear together.