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
43 changes: 43 additions & 0 deletions docs/enterprise/drag-and-drop.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Reflex Enterprise provides comprehensive drag and drop functionality for creatin

```md alert warning
# Important: Always decorate functions defining `rxe.dnd.draggable` components with `@rx.memo` to avoid compilation errors.

See [memo](/docs/library/other/memo) for how `@rx.memo` components handle parameters.
```

## Basic Usage
Expand Down Expand Up @@ -335,6 +337,8 @@ The `rxe.dnd.draggable` component makes any element draggable:
- `item`: Data object passed to drop handlers
- `on_end`: Called when drag operation ends
Comment thread
greptile-apps[bot] marked this conversation as resolved.

There is no event that fires when a drag begins — `on_end`, which fires when the drag completes, is the only drag lifecycle event on the draggable.

### Drop Target

The `rxe.dnd.drop_target` component creates areas that accept draggable items:
Expand All @@ -344,6 +348,8 @@ The `rxe.dnd.drop_target` component creates areas that accept draggable items:
- `on_drop`: Called when item is dropped
- `on_hover`: Called when item hovers over target

When drop targets are nested, a drop fires an event for each layer under the cursor, so deduplicate in the state handler (for example, with a timestamp or flag). To support dropping at the root level — such as the background of a file explorer — wrap the root container itself in a drop target.

### Collected Parameters

Access real-time drag/drop state:
Expand All @@ -355,6 +361,37 @@ Access real-time drag/drop state:
- `is_over`: Boolean indicating if draggable is hovering
- `can_drop`: Boolean indicating if drop is allowed

```md alert warning
# Assign collected params to a variable first

Do not read fields directly off the class (e.g. `rxe.dnd.DropTarget.is_over`). Assign `collected_params` to a variable, then access fields through it: `params = rxe.dnd.DropTarget.collected_params`, then `params.is_over`.
```

When a single `@rx.memo` component renders more than one draggable or drop target, give each its own collected params name to avoid JavaScript name collisions. Use `_replace(_js_expr=...)` to create a uniquely named params Var and pass it via `_collected_params`. Note that `_replace` and `_js_expr` are internal Reflex APIs and may change between releases:

```python
@rx.memo
def dual_drop_zones() -> rx.Component:
left_params = rxe.dnd.DropTarget.collected_params._replace(
_js_expr="leftZoneParams"
)
right_params = rxe.dnd.DropTarget.collected_params._replace(
_js_expr="rightZoneParams"
)
return rx.hstack(
rxe.dnd.drop_target(
rx.box("Left", bg=rx.cond(left_params.is_over, "green.100", "gray.100")),
accept=["MyDraggable"],
_collected_params=left_params,
),
Comment thread
greptile-apps[bot] marked this conversation as resolved.
rxe.dnd.drop_target(
rx.box("Right", bg=rx.cond(right_params.is_over, "green.100", "gray.100")),
accept=["MyDraggable"],
_collected_params=right_params,
),
)
```

## API Reference

### rxe.dnd.draggable
Expand Down Expand Up @@ -572,6 +609,12 @@ def app():
)
```

```md alert warning
# Do not add a second provider

Because the provider is added automatically when `draggable` or `drop_target` components are used, wrapping the app in an additional `rxe.dnd.provider` results in duplicate providers and breaks drag and drop. Only use manual control when the automatic provider does not fit (e.g. to select the touch backend), and make sure it is the only provider in the tree.
```

## Best Practices

1. **Always use `@rx.memo`** on functions containing draggable components
Expand Down
26 changes: 23 additions & 3 deletions docs/library/forms/upload.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ event args:
- `rx.upload_files(upload_id=id)` for uploads
- `rx.upload_files_chunk(upload_id=id)` for larger files that should be processed incrementally

The `id` links the upload component to these special Vars and event args, so they must all reference the same value. Pass it as a string literal or module-level constant — a state var cannot be used as an upload `id`.

## File Storage Functions

Reflex provides two key functions for handling uploaded files:
Expand All @@ -70,10 +72,12 @@ Reflex provides two key functions for handling uploaded files:

### rx.get_upload_url(filename)

- **Purpose**: Returns the URL string that can be used in frontend components to access uploaded files
- **Purpose**: Returns the URL that can be used in frontend components to access uploaded files
- **Usage**: Used in frontend components (like `rx.image`, `rx.video`) to display uploaded files
- **URL Format**: `/_upload/filename`
- **Type**: Returns `str`
- **Type**: Returns a frontend `Var` (a JavaScript expression), not a Python `str`

Because it returns a frontend Var, `rx.get_upload_url` is frontend-only: use it in component code and event triggers, not in backend event handlers or computed vars, where the value would not resolve to a usable string. If you genuinely need the upload URL as a plain string on the backend, build it from the configured upload endpoint — `Endpoint.UPLOAD.get_url()` (from `reflex.constants`) joined with the filename — which accounts for the scheme, any `backend_path` prefix, and split frontend/backend deployments.

### Key Differences

Expand Down Expand Up @@ -435,12 +439,26 @@ rx.upload.root(

## Handling the Upload

For uploads, your event handler should be an async function that accepts a single argument, `files: list[UploadFile]`, which will contain [Starlette UploadFile](https://www.starlette.io/requests/#request-files) instances. You can read the files and save them anywhere as shown in the example.
For uploads, your event handler should be an async function that accepts a single argument, `files: list[UploadFile]`, which will contain [Starlette UploadFile](https://www.starlette.io/requests/#request-files) instances. Each file exposes its original filename as `file.name`. You can read the files and save them anywhere as shown in the example.

```md alert warning
# Upload handlers are regular event handlers.
Do not declare a standard upload handler with `@rx.event(background=True)`, and do not use `async with self` inside it — those apply only to chunked upload handlers (see below). A standard handler is a plain `@rx.event` async function.
```

In your UI, you can bind the event handler to a trigger, such as a button
`on_click` event or upload `on_drop` event, and pass in the files using
`rx.upload_files()`.

```md alert warning
# Keep the upload trigger button outside the upload component.
The dropzone treats any click inside `rx.upload` / `rx.upload.root` as a request to open the file picker. If the button that triggers the upload event is placed inside, each click both opens the picker and starts the upload. Buttons inside the dropzone (like "Select File" in the examples above) should be purely visual.
```

### Uploading from a Form

A form's `on_submit` event never includes uploaded file data. When an upload component lives inside a form, trigger the upload from a separate button with `type="button"` (or from the dropzone's `on_drop` trigger) using `rx.upload_files()`, and handle the remaining form fields in `on_submit` as usual.

### Saving the File

By convention, Reflex provides the function `rx.get_upload_dir()` to get the directory where uploaded files may be saved. The upload dir comes from the environment variable `REFLEX_UPLOADED_FILES_DIR`, or `./uploaded_files` if not specified.
Expand Down Expand Up @@ -470,6 +488,8 @@ The files are automatically served at:
- `/_upload/document.pdf` ← `rx.get_upload_url("document.pdf")`
- `/_upload/video.mp4` ← `rx.get_upload_url("video.mp4")`

These paths are an implementation detail — avoid hardcoding `/upload/` or `/_upload/` in your app. Go through `rx.get_upload_url(filename)` so URLs remain correct when the frontend and backend are served from different hosts or ports.

### Chunked Uploads for Large Files

Use `rx.upload_files_chunk(...)` when files may be large or when you want the backend to write data incrementally. Standard uploads spool files to disk before the handler starts, but calling `await file.read()` in the handler loads the entire file into memory at once, which can cause high memory consumption for large files.
Expand Down
5 changes: 5 additions & 0 deletions docs/library/typography/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ For inline equations, surround the equation with `$`:
rx.markdown("Pythagorean theorem: $a^2 + b^2 = c^2$.")
```

```md alert warning
# Escaping dollar signs
Because `$` is used as the math delimiter, any pair of `$` characters in the content is interpreted as an inline LaTeX equation — the text between them may disappear or render as a garbled formula. This commonly affects prices and other monetary values, especially in dynamic content such as streamed LLM output. To render literal dollar signs, escape them before passing the text to `rx.markdown`, e.g. `content.replace("$", "\\$")`.
```

## Syntax Highlighting

You can render code blocks with syntax highlighting using the \`\`\`{language} syntax:
Expand Down
12 changes: 12 additions & 0 deletions docs/state_structure/shared_state.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,22 @@ A token can be any string that uniquely identifies a group of clients that shoul
Underscore characters are currently used as an internal delimiter for tokens and will raise an exception if used for linked states.

This is a temporary restriction and will be removed in a future release.

When deriving a token from user input, such as a query parameter, sanitize it before linking — for example `raw_token.replace("_", "-")` — and fall back to a default value when the parameter is empty or missing.
```

### Linking Shared State

An `rx.SharedState` subclass can be linked to a token using the `_link_to` method, which is async and returns the linked state instance. After linking, subsequent events triggered against the shared state will be executed in the context of the linked state. To unlink from the token, return the result of awaiting the `_unlink` method.

```md alert warning
# Common linking pitfalls

- `_link_to` is a coroutine — forgetting to `await` it silently skips linking. Any changes made to a shared state before it is linked are not shared; they apply only to the client's private instance.
- `await self._link_to(...)` returns the newly linked instance. Calling methods on `self` immediately afterwards still mutates the pre-link state, so save the returned instance and use it for any actions that should happen after linking.
- `_link_to` is only defined on `rx.SharedState` subclasses. To link from a regular `rx.State` event handler, first fetch the shared state with `await self.get_state(SharedStateCls)`, then call `_link_to` on that instance.
```

To try out the collaborative counter example, open this page in a second or third browser tab and click the "Link" button. You should see the count increment in all tabs when you click the "Increment" button in any of them.

```python demo exec
Expand Down Expand Up @@ -74,6 +84,8 @@ def shared_state_example():
Computed vars in other states may reference shared state data using `get_state`, just like private states. This allows private states to provide personalized views of shared data.

Whenever the shared state is updated, any computed vars depending on it will be re-evaluated in the context of each client's private state.

Note that a computed var that depends only on shared state vars belongs on the `rx.SharedState` itself — reserve private-state computed vars for values that also depend on per-user data.
```

### Identifying Clients
Expand Down
18 changes: 18 additions & 0 deletions docs/utility_methods/lifespan_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ Tasks execute in the order they are registered.

In dev mode, lifespan tasks will stop and restart when a hot-reload occurs.

## Lifespan Tasks vs Background Events

Lifespan tasks are not the only way to run work outside the normal event flow.
Choose based on what starts the work and what it operates on:

- Use a **lifespan task** for automatic, continuous, application-wide work that
runs independent of any user or session — it starts when the app starts and is
not tied to UI state. Examples: polling an external API on an interval,
monitoring a service, refreshing a shared cache, scheduled maintenance.
- Use a [background event](/docs/events/background-events)
(`@rx.event(background=True)`) for user-triggered, session-bound work that
reads or updates that user's UI state. Examples: processing a file after a
user clicks submit, calling an API on demand, showing progress during a
long-running operation the user initiated.

For instance, "check an external API every 5 minutes" is a lifespan task, while
"process data when the user clicks submit" is a background event.

## Tasks

Any async coroutine can be used as a lifespan task. It will be started when the
Expand Down
Loading