R

ReflexKR

Reflex Docs

Full-Stack Python Web Development with Reflex

·

Course overview

1

Meet Reflex and set up your environment

2

Drawing the screen with components

3

State and Vars: let data draw the screen

4

Events and forms: adding customers

5

Shaping data and charts

6

Wrap-up, debugging and next steps

·

Appendix

Full-Stack Python Web Development with Reflex
Lesson 6

Time 3:45–4:00

Wrap-up, debugging and next steps

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.

6-1. Today in one table (5 min)

ConceptIn codeOne line to remember
Componentrx.box(child, prop=value)Positional args are children, keyword args are props
Pageapp.add_page(index, on_load=...)One page = one function
Stateclass State(rx.State)Type annotations required; per-session server memory
VarState.users.length()Inside a UI function, it's an expression, not a value
Loops and conditionsrx.foreach, rx.condUse these instead of for and if on Vars
Event@rx.event def add_user(self, ...)The only way to change state; real Python
Formrx.form(on_submit=...) + name=name is the key in form_data
Controlled componentopen= + on_open_change=Define setters yourself (0.10)
Computed var@rx.var def ...() -> typeA value computed from other state
Chartrx.recharts.bar_chart(data=...)Data is a list[dict]

6-2. Common mistakes and fixes (5 min)

SymptomCauseFix
TypeError: object of type '...Var' has no len()len() on a Var in a UI function.length()
VarTypeError: Cannot convert Var ... to boolif / and / or on a Varrx.cond(...)
VarTypeError: Cannot iterate over Varfor on a Varrx.foreach(...)
AttributeError: ... has no attribute 'set_xxx'Assuming automatic setters exist (older tutorials)Define the setter yourself with @rx.event
Clicking Cancel still adds dataButtons inside a form default to type submittype="button"
TypeError: __init__() got an unexpected keyword argumentAn input's name doesn't match the dataclass field nameMake the names identical
The dialog won't openopen= without on_open_changeAlways use them as a pair
The chart is emptyNothing ever fills the chart dataRegister on_load or use a computed var

3 debugging habits

  1. Check the server terminal log first. Exceptions raised in handlers often don't show up clearly on the page and only get printed in the terminal.
  2. Check with uv run reflex compile --dry first. It quickly finds syntax and Var errors in your UI code without starting the server.
  3. Put 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.

6-3. Extension exercises (homework)

These are ordered by difficulty. Each one takes a concept from today a little deeper.

  1. Delete button: add a trash icon (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).
  2. Search: add a name search input, wire it up with 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.
  3. Input validation: if the email is a duplicate or badly formatted, return rx.toast.error and keep the dialog open.
  4. Persistent storage: create a 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.

6-4. What to learn next

  • Multiple pages and routing: create dynamic routes like @rx.page(route="/customers/[id]") and add a customer detail page.
  • Splitting files: break the app into models.py, state.py, components/ and pages/ so it stays manageable as it grows.
  • Deployment: try production mode with reflex run --env prod, then deploy to Reflex Cloud or your own server (Docker).
  • Official docs: reflex.dev/docs. Older blog posts may use APIs that have since changed, such as automatic setters, rx.Model or rx.App(theme=...), so check the version before you follow along.

← Previous

Lesson 5 · Shaping data and charts

Next →

Appendix

Get news about new lessons

We'll email you when this course gets new lessons or a follow-up course opens.

We collect: your email address · Purpose: news about new lessons or the launch of this course · Kept until: you ask us to remove it. We never use it for anything else or share it. Reply to any notice email and we'll delete it right away.

Questions & answers

Ask anything about this lesson. Questions from every language version of this page appear together.

Shown as plain text, without links or formatting.

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
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex