Time 3:45–4:00
The app you built today runs on a single loop: state (State) → UI (Vars) → events (handlers) → state change. No matter how big a Reflex app gets, it's just more of this same loop.
| Concept | In code | One line to remember |
|---|---|---|
| Component | rx.box(child, prop=value) | Positional args are children, keyword args are props |
| Page | app.add_page(index, on_load=...) | One page = one function |
| State | class State(rx.State) | Type annotations required; per-session server memory |
| Var | State.users.length() | Inside a UI function, it's an expression, not a value |
| Loops and conditions | rx.foreach, rx.cond | Use these instead of for and if on Vars |
| Event | @rx.event def add_user(self, ...) | The only way to change state; real Python |
| Form | rx.form(on_submit=...) + name= | name is the key in form_data |
| Controlled component | open= + on_open_change= | Define setters yourself (0.10) |
| Computed var | @rx.var def ...() -> type | A value computed from other state |
| Chart | rx.recharts.bar_chart(data=...) | Data is a list[dict] |
| Symptom | Cause | Fix |
|---|---|---|
TypeError: object of type '...Var' has no len() | len() on a Var in a UI function | .length() |
VarTypeError: Cannot convert Var ... to bool | if / and / or on a Var | rx.cond(...) |
VarTypeError: Cannot iterate over Var | for on a Var | rx.foreach(...) |
AttributeError: ... has no attribute 'set_xxx' | Assuming automatic setters exist (older tutorials) | Define the setter yourself with @rx.event |
| Clicking Cancel still adds data | Buttons inside a form default to type submit | type="button" |
TypeError: __init__() got an unexpected keyword argument | An input's name doesn't match the dataclass field name | Make the names identical |
| The dialog won't open | open= without on_open_change | Always use them as a pair |
| The chart is empty | Nothing ever fills the chart data | Register on_load or use a computed var |
3 debugging habits
uv run reflex compile --dry first. It quickly finds syntax and Var errors in your UI code without starting the server.print() inside handlers. Handlers are Python running on the server, so print(form_data) shows up in the server terminal. A print inside a UI function prints only once, when the app starts.These are ordered by difficulty. Each one takes a concept from today a little deeper.
rx.icon("trash-2")) at the end of each row that deletes that customer when clicked. Hint: pass an argument, like on_click=lambda: State.delete_user(user.email).value= + on_change= (a controlled component), and show the list filtered by a computed var in the table. Try on_change=State.set_search.debounce(300) so the search runs only after typing stops.rx.toast.error and keep the dialog open.Customer table with SQLModel, read from it in on_load and write to it in add_user. Customers should still be there after a refresh or a server restart. Install it with uv add "reflex[db]"; in Reflex 0.10, using SQLModel directly is recommended over rx.Model.@rx.page(route="/customers/[id]") and add a customer detail page.models.py, state.py, components/ and pages/ so it stays manageable as it grows.reflex run --env prod, then deploy to Reflex Cloud or your own server (Docker).rx.Model or rx.App(theme=...), so check the version before you follow along.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.