Skip to content
Open
Show file tree
Hide file tree
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
9 changes: 9 additions & 0 deletions docs/api-reference/browser_storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ class CookieState(rx.State):
c6: str = rx.Cookie(name="c6-custom-name")
```

```md alert info
# The default value is the first positional argument.

`rx.Cookie`, `rx.LocalStorage`, and `rx.SessionStorage` all take the default
value as their first positional argument, e.g. `rx.LocalStorage("light")`. It
cannot be passed as a keyword argument — the keyword arguments (`name`,
`max_age`, `sync`, etc.) only configure the storage behavior.
```

```md alert warning
# **The default value of a Cookie is never set in the browser!**

Expand Down
57 changes: 57 additions & 0 deletions docs/api-reference/event_triggers.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,60 @@ def scroll_example():
width="100%",
)
```

## on_key_down

The on_key_down event handler is called when the user presses a key while the element has focus. The handler receives the name of the key (e.g. `"Enter"`, `"Escape"`, `"a"`, `"ArrowUp"`) and a dictionary describing the active modifier keys (`alt_key`, `ctrl_key`, `meta_key`, `shift_key`).

A handler may accept only the key name if it does not need the modifier info.

```python demo exec
class KeyDownState(rx.State):
message: str = ""

@rx.event
def handle_key_down(self, key: str):
if key == "Enter":
self.message = "You pressed Enter!"
else:
self.message = f"Last key pressed: {key}"


def key_down_example():
return rx.vstack(
rx.text(KeyDownState.message),
rx.input(
placeholder="Focus me and press a key...",
on_key_down=KeyDownState.handle_key_down,
),
)
```

## on_key_up

The on_key_up event handler is called when the user releases a key. It receives the same arguments as `on_key_down`.

## Global Keyboard Events

`on_key_down` on an element only fires while that element has focus. To listen for keyboard events anywhere on the page — for example, to implement global hotkeys — attach `on_key_down` to the `rx.window_event_listener` component, which listens on the browser window and renders no visible output.

```python
class HotkeyState(rx.State):
last_key_pressed: str = ""

@rx.event
def on_hotkey_press(self, key: str):
if key not in ("w", "a", "s", "d"):
return
self.last_key_pressed = key


def hotkey_example():
return rx.vstack(
rx.text("Press w, a, s, or d anywhere on the page."),
rx.text(f"Last hotkey pressed: {HotkeyState.last_key_pressed}"),
rx.window_event_listener(
on_key_down=HotkeyState.on_hotkey_press,
),
)
```
48 changes: 48 additions & 0 deletions docs/api-reference/special_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ rx.button("Scroll to download button", on_click=rx.scroll_to("download button"))

When this is triggered, it scrolls to an element passed by id as parameter. Click on button to scroll to download button (rx.download section) at the bottom of the page

To keep a container automatically scrolled to the bottom as new content is added — for example a chat or log view — use the [`rx.auto_scroll`](/docs/library/dynamic-rendering/auto-scroll/) component instead.

## rx.redirect

Redirect the user to a new path within the application.
Expand Down Expand Up @@ -86,6 +88,29 @@ This event allows you to copy a given text or content to the user's clipboard.
It's handy when you want to provide a "Copy to Clipboard" feature in your application,
allowing users to easily copy information to paste elsewhere.

`rx.set_clipboard` also accepts state Vars, so you can copy dynamic content,
such as the current value of an input field:

```python demo exec
class CopyState(rx.State):
text: str = ""

@rx.event
def set_text(self, value: str):
self.text = value


def copy_input_example():
return rx.hstack(
rx.input(
placeholder="Type something to copy",
value=CopyState.text,
on_change=CopyState.set_text,
),
rx.button("Copy", on_click=rx.set_clipboard(CopyState.text)),
)
```

## rx.set_value

Set the value of a specified reference element.
Expand Down Expand Up @@ -130,3 +155,26 @@ rx.button(
id="download button",
)
```

When the data to download is not already available at a known URL, return
`rx.download` from an event handler and pass the `data` directly:

```python demo exec
import random


class DownloadState(rx.State):
@rx.event
def download_random_data(self):
return rx.download(
data=",".join([str(random.randint(0, 100)) for _ in range(10)]),
filename="random_numbers.csv",
)


def download_random_data_button():
return rx.button(
"Download random numbers",
on_click=DownloadState.download_random_data,
)
```
39 changes: 39 additions & 0 deletions docs/events/page_load_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,42 @@ class State(rx.State):
def index():
return rx.text("A Beautiful App")
```

## Handling Loading and Errors

Avoid heavy synchronous work directly in an `on_load` handler — the page renders before the handler finishes, so a slow handler leaves the user staring at stale or empty data with no feedback. Instead:

- Use an async handler for network or database calls so the event loop is not blocked.
- Set a loading flag and `yield` to send it to the frontend immediately, then render a spinner or placeholder with `rx.cond`.
- Wrap the work in `try`/`except` so a failure surfaces as an error message instead of a page that never finishes loading.

```python
class DataState(rx.State):
data: dict = {}
loading: bool = False
error: str = ""

@rx.event
async def load_initial_data(self):
self.loading = True
yield # Send the loading state to the frontend immediately.
try:
self.data = await fetch_data()
except Exception as e:
self.error = str(e)
finally:
self.loading = False


@rx.page(on_load=DataState.load_initial_data)
def index():
return rx.cond(
DataState.loading,
rx.spinner(),
rx.cond(
DataState.error != "",
rx.text(f"Error: {DataState.error}"),
rx.text(f"Data loaded: {DataState.data}"),
),
)
```
48 changes: 48 additions & 0 deletions docs/library/forms/text_area.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,51 @@ def feedback_form():
),
)
```

## Submitting on Enter

By default, pressing Enter in a text area inserts a new line. Set the
`enter_key_submit` prop to submit the enclosing form when Enter is pressed
instead. Shift+Enter still inserts a new line, so multi-line input remains
possible.

```python demo exec
class EnterSubmitState(rx.State):
submitted_text: str = ""

@rx.event
def handle_submit(self, form_data: dict):
self.submitted_text = form_data["message"]


def enter_key_submit_example():
return rx.vstack(
rx.cond(
EnterSubmitState.submitted_text != "",
rx.text("Submitted: ", EnterSubmitState.submitted_text),
rx.text("Nothing submitted yet."),
),
rx.form(
rx.text_area(
name="message",
placeholder="Type and press Enter to submit",
enter_key_submit=True,
),
on_submit=EnterSubmitState.handle_submit,
),
width="100%",
)
```

`enter_key_submit` accepts a Var, so it can be toggled dynamically — for
example, disabling Enter-to-submit while a previous submission is still being
processed: `enter_key_submit=~State.is_loading`.

```md alert warning
# `enter_key_submit` cannot be combined with `on_key_down`.

The `enter_key_submit` prop is implemented with its own key down handler, so
passing both to the same component raises an error. If custom key handling is
needed, implement the Enter-to-submit behavior in your own `on_key_down`
handler instead.
Comment on lines +136 to +139

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't suggest on_key_down can replace enter_key_submit

In the case where a textarea also needs custom key handling, this fallback tells users to implement the Enter-submit behavior with a normal Reflex on_key_down, but the built-in implementation needs the raw JS event to call e.preventDefault() and e.target.form.requestSubmit() (packages/reflex-components-core/src/reflex_components_core/el/elements/forms.py:870-877), while the public key event only sends the key and modifier Vars to state handlers (packages/reflex-base/src/reflex_base/event/__init__.py:1028-1045). Following this advice still lets Enter insert a newline, and using .prevent_default would suppress all key input rather than only Enter, so the docs should point to a custom JS custom_attrs handler or another supported pattern.

Useful? React with 👍 / 👎.

```
42 changes: 42 additions & 0 deletions docs/library/other/memo.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,48 @@ def index():
)
```

## Using with `rx.foreach`

To render a memoized component for each item of a list Var, wrap the call in a
lambda and pass the props by keyword. Also pass a `key` prop that uniquely
identifies each item — React uses it to track items across re-renders — but do
**not** declare `key` in the memo function's signature. It is forwarded
automatically and consumed by React.

```python
from typing import TypedDict


class Task(TypedDict):
id: str
name: str


class TaskState(rx.State):
tasks: list[Task] = [
{"id": "1", "name": "Write docs"},
{"id": "2", "name": "Review PR"},
]


@rx.memo
def task_card(task: rx.Var[Task]) -> rx.Component:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid documenting deprecated key forwarding

When users copy this rx.foreach example, the later key=... is passed to a memo that has no rx.RestProp. The runtime explicitly treats that as legacy and calls _warn_legacy_base_props when rest_param is None (packages/reflex-base/src/reflex_base/components/memo.py:1630-1643), with the deprecation message saying to declare rx.RestProp for base props (packages/reflex-base/src/reflex_base/components/memo.py:1869-1878). This doc therefore recommends a pattern that immediately emits a deprecation warning and is scheduled for removal; include and pass a RestProp while still not declaring key as an explicit prop, or call out the warning.

Useful? React with 👍 / 👎.

return rx.card(rx.text(task["name"]))


def index():
return rx.vstack(
rx.foreach(
TaskState.tasks,
lambda task: task_card(task=task, key=task["id"]),
),
)
```
Comment on lines +74 to +107

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Deprecated key forwarding pattern documented as recommended

The example passes key=task["id"] to task_card which has no rx.RestProp parameter. In packages/reflex-base/src/reflex_base/components/memo.py (lines 1630–1643), this path hits _warn_legacy_base_props, emitting a console.deprecate call with deprecation_version="0.9.3" and removal_version="1.0". The deprecation message explicitly tells users to "Declare an rx.RestProp parameter to keep passing base props like key". Any user who follows this doc example as written will see a deprecation warning on every render, and the pattern will stop working entirely in 1.0.

The example should add rest: rx.RestProp to task_card's signature so the key is forwarded through the ...rest spread without triggering the warning, or at minimum include a note that this usage is deprecated and show the rx.RestProp alternative.


Inside the memo body, `task` is a `Var`, not a plain dict: index into it with
`task["name"]` or use it in f-strings, but do not iterate over it or call
Python dict methods like `.keys()` — only Var operations are available.

## Forwarding Props with `rx.RestProp`

Use `rx.RestProp` to accept and forward arbitrary props (think `...rest` in JSX). Useful for thin wrappers that re-style a primitive without redeclaring every prop.
Expand Down
Loading