Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 91 additions & 57 deletions docs/library/dynamic-rendering/foreach.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,64 @@
---
components:
- rx.foreach
---

```python exec
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.
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 alert warning
# `rx.foreach` is specialized for usecases where the iterable is defined in a state var.

For an iterable where the content doesn't change at runtime, i.e a constant, using a list/dict comprehension instead of `rx.foreach` is preferred.
# Use `rx.foreach` for state vars; use Python list or dict comprehensions for constants.
```

```python demo exec
from typing import List


class ForeachState(rx.State):
color: List[str] = ["red", "green", "blue", "yellow", "orange", "purple"]
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: str):
return rx.box(rx.text(color), bg=color)
def colored_box(color: rx.Var[str]):
return color_swatch(color, color)


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

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

```python demo exec
def colored_box_index(color: str, index: int):
return rx.box(rx.text(index), bg=color)
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.color, lambda color, index: colored_box_index(color, index)
ForeachState.colors, lambda color, index: colored_box_index(color, index)
),
columns="2",
)
Expand All @@ -59,14 +70,11 @@ When indexing into a nested list, it's important to declare the list's type as R
This ensures that any potential frontend JS errors are caught before the user can encounter them.

```python demo exec
from typing import List


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


def display_row(row):
def display_row(row: rx.Var[list[str]]):
return rx.hstack(
rx.foreach(
row,
Expand All @@ -86,36 +94,49 @@ def nested_foreach_example():
Below is a more complex example of foreach within a todo list.

```python demo exec
from typing import List
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[str] = ["Write Code", "Sleep", "Have Fun"]
new_item: str
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 += [self.new_item]
self.items += [TodoItem(text=self.new_item)]

def finish_item(self, item: str):
self.items = [i for i in self.items if i != 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):
def get_item(item: rx.Var[TodoItem]):
return rx.list.item(
rx.hstack(
rx.button(
on_click=lambda: ListState.finish_item(item),
height="1.5em",
background_color="white",
border="1px solid blue",
"Done",
on_click=lambda: ListState.finish_item(item.id),
size="1",
variant="soft",
),
rx.text(item, font_size="1.25em"),
rx.text(item.text, font_size="1.25em"),
),
key=item.id,
)


Expand All @@ -125,7 +146,7 @@ def todo_example():
rx.input(
on_blur=ListState.set_new_item, placeholder="Add a todo...", bg="white"
),
rx.button("Add", on_click=ListState.add_item, bg="white"),
rx.button("Add", on_click=ListState.add_item),
rx.divider(),
rx.list.ordered(
rx.foreach(
Expand All @@ -142,20 +163,18 @@ def todo_example():

## Dictionaries

Items in a dictionary can be accessed as list of key-value pairs.
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 demo exec
from typing import List


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


def display_color(color: List):
# color is presented as a list key-value pair([1, "blue"],[2, "red"], [3, "green"])
return rx.box(rx.text(color[0]), bg=color[1])
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():
Expand All @@ -165,31 +184,46 @@ def foreach_dict_example():
```

Now let's show a more complex example with dicts using the color example.
Assuming we want to display a dictionary of secondary colors as keys and their constituent primary colors as values, we can modify the code as below:
This example groups related hex colors in a dictionary and renders both the keys and values as swatches:

```python demo exec
from typing import List, Dict


class ComplexDictForeachState(rx.State):
color_chart: Dict[str, List[str]] = {
"purple": ["red", "blue"],
"orange": ["yellow", "red"],
"green": ["blue", "yellow"],
color_chart: dict[str, list[str]] = {
"#8E4EC6": ["#E5484D", "#3E63DD"],
"#F76B15": ["#AD5700", "#E5484D"],
"#12A594": ["#3E63DD", "#AD5700"],
}


def display_colors(color: List):
def display_colors(color: rx.Var[tuple[str, list[str]]]):
return rx.vstack(
rx.text(color[0], color=color[0]),
color_swatch(color[0], color[0]),
rx.hstack(
rx.foreach(color[1], lambda x: rx.box(rx.text(x, color="black"), bg=x))
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="2"
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.
Loading