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 3

Time 1:25–2:10

State and Vars: let data draw the screen

In this lesson, you'll put the customer data in State and have the table and badge render from that data. This is where you learn the most important concept in Reflex: the Var.

3-1. Define the shape of the data: dataclass (5 min)

import dataclasses


@dataclasses.dataclass
class User:
    name: str
    email: str
    gender: str
  • This is the "blueprint" for which fields a customer has. You create one with User(name="...", email="...", gender="...").
  • A dictionary ({"name": ...}) would work too, but with a dataclass your editor catches typos in field names, and you can use dot access like user.name in your UI code.

3-2. The State class (10 min)

class State(rx.State):
    users: list[User] = [
        User(name="Danilo Sousa", email="danilo@example.com", gender="Male"),
        User(name="Zahra Ambessa", email="zahra@example.com", gender="Female"),
    ]
  • Class variables on a class that inherits from rx.State become state vars. The values you write here are the initial values.
  • Type annotations (: list[User]) are required. Reflex uses the type to decide how to send the value to the browser (as JSON) and which methods you can call on it.
  • State lives in server memory, separately for each browser session. Customers you add aren't visible to anyone else, and since this isn't a database, they aren't saved permanently either. Persistent storage is covered in the extension exercises in Lesson 6.

3-3. Var: not a "value" but an "expression sent to the page" (15 min)

In Lesson 1 we said "UI functions run only once, when the app starts." So what do you get when you use State.users inside a UI function? Here's what printing it actually shows:

>>> type(State.users).__name__
'ArrayCastedVar'
>>> print(State.users.length())
reflex___state____state__tutorial_261019___tutorial_261019____state.users_rx_state_.length

Instead of a list of customers, you get a JavaScript expression. That's a Var. When the UI function runs, there are no users and no data yet, so all it can do is leave a promise: "read this value in the browser later."

That's why some Python syntax doesn't work on Vars. Here are the errors you actually get in Reflex 0.10, and what to use instead.

What you wantCode that failsError you getThe Reflex way
Count itemslen(State.users)TypeError: object of type 'ArrayCastedVar' has no len()State.users.length()
Branch on a condition"a" if State.n > 1 else "b"VarTypeError: Cannot convert Var ... to boolrx.cond(State.n > 1, "a", "b")
Loopfor u in State.users:VarTypeError: Cannot iterate over Var ...rx.foreach(State.users, show_user)
Complex transformationsComputing inside a UI function—Compute in an event handler or an @rx.var (computed var)

On the other hand, comparisons and arithmetic between Vars (State.page + 1) and Vars inside f-strings (f"Total: {State.total}") work fine, because the operations themselves are translated into JavaScript expressions.

Instructor note — This table is the single most important thing in the course. Most of the errors beginners hit in Reflex come from here. On video, we recommend deliberately writing len(State.users) to show the error, asking "Why doesn't this work?", and then answering with the print output above.

Now replace the placeholder badge in the header with the real customer count.

rx.badge(
    State.users.length(),     # a Var instead of "2"
    variant="soft",
    color_scheme="gray",
    radius="full",
),

3-4. A function that draws one row: show_user (5 min)

def show_user(user: User):
    return rx.table.row(
        rx.table.cell(user.name),
        rx.table.cell(user.email),
        rx.table.cell(user.gender),
        style={"_hover": {"bg": rx.color("gray", 3)}},
        align="center",
    )
  • The type says User, but what actually comes in is a Var pointing to a User. user.name also becomes an expression meaning "read the name of this row's customer." Dot access works because you declared the fields with a dataclass.
  • style={"_hover": {...}} is the style applied on mouse hover. Keys that start with _, like _hover and _focus, are state selectors like CSS :hover.

3-5. The table and rx.foreach (10 min)

Add the table as the second child inside the card, below the header.

rx.box(
    rx.table.root(
        rx.table.header(
            rx.table.row(
                rx.table.column_header_cell("Name"),
                rx.table.column_header_cell("Email"),
                rx.table.column_header_cell("Gender"),
            )
        ),
        rx.table.body(rx.foreach(State.users, show_user)),
        variant="ghost",
        size="3",
        width="100%",
    ),
    overflow_x="auto",                                   # horizontal scroll on narrow screens
    width="100%",
    border_top=f"1px solid {rx.color('slate', 4)}",
    border_bottom=f"1px solid {rx.color('slate', 4)}",
    padding_x="12px",
),
  • Table structure: root > header (the title row) + body (the data rows) > row > cell. These map to HTML <table>, <thead>, <tbody>, <tr> and <td>.
  • rx.foreach(list Var, function) applies the function to each item of the list in the browser. You pass the function by name without calling it (show_user, not show_user()).
  • For a fixed list like the header row, though, a regular Python for is fine, because the values are known at compile time. For example: *[rx.table.column_header_cell(h) for h in ["Name", "Email", "Gender"]]

Check your browser — A table with 2 customers appears inside the card, and the badge's 2 now comes from State.users.length(). Hover over a row and its background turns light gray.

localhost:3000

Live preview

Customers

2

Your customer directory and audience breakdown.

NameEmailGender
Danilo Sousadanilo@example.comMale
Zahra Ambessazahra@example.comFemale
End of Lesson 3: the 2 customers in State rendered as a table

Exercises

  1. Add one more customer to the initial value and check that the badge changes to 3.
  2. Use rx.cond to show "No customers yet" instead of the table when there are 0 customers. Hint: rx.cond(State.users.length() > 0, table, rx.text(...))
  3. Write len(State.users) on purpose and read the error message yourself.

Checkpoint

  • The table shows 2 customers and the badge shows 2.
  • You can explain what a Var is and why you can't use len/if/for on it.
  • You know why you pass a function to rx.foreach without calling it.

← Previous

Lesson 2 · Drawing the screen with components

Next →

Lesson 4 · Events and forms: adding customers

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