Back to Reflex

Foreach

docs/library/dynamic-rendering/foreach.md

0.9.4a15.9 KB
Original Source
python
import reflex as rx

Foreach

The rx.foreach component takes an iterable (list, tuple, or dict) and a function that renders each item in the list. This is useful for dynamically rendering a list of items defined in a state.

md
# Use `rx.foreach` for state vars; use Python list or dict comprehensions for constants.
python
class ForeachState(rx.State):
    colors: list[str] = [
        "#E5484D",
        "#12A594",
        "#3E63DD",
        "#AD5700",
        "#F76B15",
        "#8E4EC6",
    ]


def color_swatch(label: rx.Var[str | int], color: rx.Var[str]):
    return rx.box(
        rx.text(label, color="white", weight="medium"),
        bg=color,
        padding_y="0.5em",
        padding_x="0.75em",
        min_width="5.5em",
        text_align="center",
        border_radius="0.375rem",
        border="1px solid rgba(0, 0, 0, 0.12)",
        box_shadow="0 1px 2px rgba(0, 0, 0, 0.10)",
    )


def colored_box(color: rx.Var[str]):
    return color_swatch(color, color)


def foreach_example():
    return rx.grid(
        rx.foreach(ForeachState.colors, colored_box),
        columns="2",
    )

The function can also take an index as a second argument.

python
def colored_box_index(color: rx.Var[str], index: rx.Var[int]):
    return color_swatch(index, color)


def foreach_example_index():
    return rx.grid(
        rx.foreach(
            ForeachState.colors, lambda color, index: colored_box_index(color, index)
        ),
        columns="2",
    )

Nested foreach components can be used to render nested lists.

When indexing into a nested list, it's important to declare the list's type as Reflex requires it for type checking. This ensures that any potential frontend JS errors are caught before the user can encounter them.

python
class NestedForeachState(rx.State):
    numbers: list[list[str]] = [["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]


def display_row(row: rx.Var[list[str]]):
    return rx.hstack(
        rx.foreach(
            row,
            lambda item: rx.box(
                item,
                border="1px solid black",
                padding="0.5em",
            ),
        ),
    )


def nested_foreach_example():
    return rx.vstack(rx.foreach(NestedForeachState.numbers, display_row))

Below is a more complex example of foreach within a todo list.

python
from dataclasses import dataclass, field
from uuid import UUID, uuid4


@dataclass
class TodoItem:
    text: str
    id: UUID = field(default_factory=uuid4)


class ListState(rx.State):
    items: list[TodoItem] = [
        TodoItem(text="Write Code"),
        TodoItem(text="Sleep"),
        TodoItem(text="Have Fun"),
    ]
    new_item: str = ""

    @rx.event
    def set_new_item(self, new_item: str):
        self.new_item = new_item

    @rx.event
    def add_item(self):
        self.items += [TodoItem(text=self.new_item)]

    @rx.event
    def finish_item(self, item_id: UUID):
        self.items = [item for item in self.items if item.id != item_id]


def get_item(item: rx.Var[TodoItem]):
    return rx.list.item(
        rx.hstack(
            rx.button(
                "Done",
                on_click=lambda: ListState.finish_item(item.id),
                size="1",
                variant="soft",
            ),
            rx.text(item.text, font_size="1.25em"),
        ),
        key=item.id,
    )


def todo_example():
    return rx.vstack(
        rx.heading("Todos"),
        rx.input(
            on_blur=ListState.set_new_item, placeholder="Add a todo...", bg="white"
        ),
        rx.button("Add", on_click=ListState.add_item),
        rx.divider(),
        rx.list.ordered(
            rx.foreach(
                ListState.items,
                get_item,
            ),
        ),
        bg="#ededed",
        padding="1em",
        border_radius="0.5em",
        shadow="lg",
    )

Dictionaries

Items in a dictionary are passed to the render function as key-value pairs. When iterating over a dict, keys are coerced to strings in the foreach callback, even when the Python dictionary uses another key type. Using the color example, we can slightly modify the code to use dicts as shown below.

python
class SimpleDictForeachState(rx.State):
    color_chart: dict[int, str] = {1: "#3E63DD", 2: "#E5484D", 3: "#12A594"}


def display_color(color: rx.Var[tuple[str, str]]):
    # color is presented as a key-value pair such as ("1", "#3E63DD").
    return color_swatch(color[0], color[1])


def foreach_dict_example():
    return rx.grid(
        rx.foreach(SimpleDictForeachState.color_chart, display_color), columns="2"
    )

Now let's show a more complex example with dicts using the color example. This example groups related hex colors in a dictionary and renders both the keys and values as swatches:

python
class ComplexDictForeachState(rx.State):
    color_chart: dict[str, list[str]] = {
        "#8E4EC6": ["#E5484D", "#3E63DD"],
        "#F76B15": ["#AD5700", "#E5484D"],
        "#12A594": ["#3E63DD", "#AD5700"],
    }


def display_colors(color: rx.Var[tuple[str, list[str]]]):
    return rx.vstack(
        color_swatch(color[0], color[0]),
        rx.hstack(
            rx.foreach(
                color[1],
                lambda x: color_swatch(x, x),
            )
        ),
        align="center",
        spacing="2",
    )


def foreach_complex_dict_example():
    return rx.grid(
        rx.foreach(ComplexDictForeachState.color_chart, display_colors),
        columns="3",
        spacing="4",
    )

API Reference

rx.foreach

python
rx.foreach(iterable, render_fn)
  • iterable: A state var or iterable to render. Lists, tuples, sets, strings, and dicts are supported; dicts are passed to render_fn as key-value tuples with string keys.
  • render_fn: A function that returns a component for each item. It receives the item as the first rx.Var[...] argument and, optionally, the index as a second rx.Var[int] argument.