# Reflex Documentation Source: http://localhost:3000/docs/ This file stitches together the full Reflex documentation as Markdown for AI agents and LLM indexing. For a navigable index with links to individual docs pages, see [llms.txt](http://localhost:3000/docs/llms.txt). # Project Structure (Advanced) Source: http://localhost:3000/docs/advanced-onboarding/code-structure.md ## App Module Reflex imports the main app module based on the `app_name` from the config, which **must define a module-level global named `app` as an instance of `rx.App`**. The main app module is responsible for importing all other modules that make up the app and defining `app = rx.App()`. **All other modules containing pages, state, and models MUST be imported by the main app module or package** for Reflex to include them in the compiled output. ## Breaking the App into Smaller Pieces As applications scale, effective organization is crucial. This is achieved by breaking the application down into smaller, manageable modules and organizing them into logical packages that avoid circular dependencies. In the following documentation there will be an app with an `app_name` of `example_big_app`. The main module would be `example_big_app/example_big_app.py`. In the [Putting it all together](#putting-it-all-together) section there is a visual of the project folder structure to help follow along with the examples below. ### Pages Package: `example_big_app/pages` All complex apps will have multiple pages, so it is recommended to create `example_big_app/pages` as a package. 1. This package should contain one module per page in the app. 2. If a particular page depends on the state, the substate should be defined in the same module as the page. 3. The page-returning function should be decorated with `rx.page()` to have it added as a route in the app. ```python import reflex as rx from ..state import AuthState class LoginState(AuthState): @rx.event def handle_submit(self, form_data): self.logged_in = authenticate(form_data["username"], form_data["password"]) def login_field(name: str, **input_props): return rx.hstack( rx.text(name.capitalize()), rx.input(name=name, **input_props), width="100%", justify="between", ) @rx.page(route="/login") def login(): return rx.card( rx.form( rx.vstack( login_field("username"), login_field("password", type="password"), rx.button("Login"), width="100%", justify="center", ), on_submit=LoginState.handle_submit, ), ) ``` ### Templating: `example_big_app/template.py` Most applications maintain a consistent layout and structure across pages. Defining this common structure in a separate module facilitates easy sharing and reuse when constructing individual pages. **Best Practices** 1. Factor out common frontend UI elements into a function that returns a component. 2. If a function accepts a function that returns a component, it can be used as a decorator as seen below. ```python from typing import Callable import reflex as rx from .components.menu import menu from .components.navbar import navbar def template(page: Callable[[], rx.Component]) -> rx.Component: return rx.vstack( navbar(), rx.hstack( menu(), rx.container(page()), ), width="100%", ) ``` The `@template` decorator should appear below the `@rx.page` decorator and above the page-returning function. See the [Posts Page](#a-post-page-example_big_apppagespostspy) code for an example. ## State Management Most pages will use State in some capacity. You should avoid adding vars to a shared state that will only be used in a single page. Instead, define a new subclass of `rx.State` and keep it in the same module as the page. ### Accessing other States As of Reflex 0.4.3, any event handler can get access to an instance of any other substate via the `get_state` API. From a practical perspective, this means that state can be split up into smaller pieces without requiring a complex inheritance hierarchy to share access to other states. In previous releases, if an app wanted to store settings in `SettingsState` with a page or component for modifying them, any other state with an event handler that needed to access those settings would have to inherit from `SettingsState`, even if the other state was mostly orthogonal. The other state would also now always have to load the settings, even for event handlers that didn't need to access them. A better strategy is to load the desired state on demand from only the event handler which needs access to the substate. ### A Settings Component: `example_big_app/components/settings.py` ```python import reflex as rx class SettingsState(rx.State): refresh_interval: int = 15 auto_update: bool = True prefer_plain_text: bool = True posts_per_page: int = 20 def settings_dialog(): return rx.dialog(...) ``` ### A Post Page: `example_big_app/pages/posts.py` This page loads the `SettingsState` to determine how many posts to display per page and how often to refresh. ```python import reflex as rx from ..models import Post from ..template import template from ..components.settings import SettingsState class PostsState(rx.State): refresh_tick: int page: int posts: list[Post] @rx.event async def on_load(self): settings = await self.get_state(SettingsState) if settings.auto_update: self.refresh_tick = settings.refresh_interval * 1000 else: self.refresh_tick = 0 @rx.event async def tick(self, _): settings = await self.get_state(SettingsState) with rx.session() as session: q = ( Post .select() .offset(self.page * settings.posts_per_page) .limit(settings.posts_per_page) ) self.posts = q.all() @rx.event def go_to_previous(self): if self.page > 0: self.page = self.page - 1 @rx.event def go_to_next(self): if self.posts: self.page = self.page + 1 @rx.page(route="/posts", on_load=PostsState.on_load) @template def posts(): return rx.vstack( rx.foreach(PostsState.posts, post_view), rx.hstack( rx.button("< Prev", on_click=PostsState.go_to_previous), rx.button("Next >", on_click=PostsState.go_to_next), justify="between", ), rx.moment( interval=PostsState.refresh_tick, on_change=PostsState.tick, display="none" ), width="100%", ) ``` ### Common State: `example_big_app/state.py` _Common_ states and substates that are shared by multiple pages or components should be implemented in a separate module to avoid circular imports. This module should not import other modules in the app. ## Component Reusability The primary mechanism for reusing components in Reflex is to define a function that returns the component, then simply call it where that functionality is needed. Component functions typically should not take any State classes as arguments, but prefer to import the needed state and access the vars on the class directly. ### Memoize Functions for Improved Performance In a large app, if a component has many subcomponents or is used in a large number of places, it can improve compile and runtime performance to memoize the function with the `@lru_cache` decorator. To memoize the `foo` component to avoid re-creating it many times simply add `@lru_cache` to the function definition, and the component will only be created once per unique set of arguments. ```python from functools import lru_cache import reflex as rx class State(rx.State): v: str = "foo" @rx.event def set_v(self, value: str): self.v = value @lru_cache def foo(): return rx.text(State.v) def index(): return rx.flex( rx.button( "Change", on_click=State.set_v(rx.cond(State.v != "bar", "bar", "foo")) ), *[foo() for _ in range(100)], direction="row", wrap="wrap", ) ``` ### example_big_app/components This package contains reusable parts of the app, for example headers, footers, and menus. If a particular component requires state, the substate may be defined in the same module for locality. Any substate defined in a component module should only contain fields and event handlers pertaining to that individual component. ### External Components Reflex 0.4.3 introduced support for the [`reflex component` CLI commands](/docs/custom-components/overview), which makes it easy to bundle up common functionality to publish on PyPI as a standalone Python package that can be installed and used in any Reflex app. When wrapping npm components or other self-contained bits of functionality, it can be helpful to move this complexity outside the app itself for easier maintenance and reuse in other apps. ## Database Models: `example_big_app/models.py` It is recommended to implement all database models in a single file to make it easier to define relationships and understand the entire schema. However, if the schema is very large, it might make sense to have a `models` package with individual models defined in their own modules. At any rate, defining the models separately allows any page or component to import and use them without circular imports. ## Top-level Package: `example_big_app/__init__.py` This is a great place to import all state, models, and pages that should be part of the app. Typically, components and helpers do not need to imported, because they will be imported by pages that use them (or they would be unused). ```python from . import state, models from .pages import index, login, post, product, profile, schedule __all__ = [ "state", "models", "index", "login", "post", "product", "profile", "schedule", ] ``` If any pages are not imported here, they will not be compiled as part of the app. ## example_big_app/example_big_app.py This is the main app module. Since everything else is defined in other modules, this file becomes very simple. ```python import reflex as rx app = rx.App() ``` ## File Management There are two categories of non-code assets (media, fonts, stylesheets, documents) typically used in a Reflex app. ### assets The `assets` directory is used for **static** files that should be accessible relative to the root of the frontend (default port 3000). When an app is deployed in production mode, changes to the assets directory will NOT be available at runtime! When referencing an asset, always use a leading forward slash, so the asset can be resolved regardless of the page route where it may appear. ### uploaded_files If an app needs to make files available dynamically at runtime, it is recommended to set the target directory via `REFLEX_UPLOADED_FILES_DIR` environment variable (default `./uploaded_files`), write files relative to the path returned by `rx.get_upload_dir()`, and create working links via `rx.get_upload_url(relative_path)`. Uploaded files are served from the backend (default port 8000) via `/_upload/` ## Putting it all together Based on the previous discussion, the recommended project layout look like this. ```text example-big-app/ ├─ assets/ ├─ example_big_app/ │ ├─ components/ │ │ ├─ __init__.py │ │ ├─ auth.py │ │ ├─ footer.py │ │ ├─ menu.py │ │ ├─ navbar.py │ ├─ pages/ │ │ ├─ __init__.py │ │ ├─ index.py │ │ ├─ login.py │ │ ├─ posts.py │ │ ├─ product.py │ │ ├─ profile.py │ │ ├─ schedule.py │ ├─ __init__.py │ ├─ example_big_app.py │ ├─ models.py │ ├─ state.py │ ├─ template.py ├─ uploaded_files/ ├─ pyproject.toml ├─ rxconfig.py ``` ## Key Takeaways - Like any other Python project, **split up the app into modules and packages** to keep the codebase organized and manageable. - Using smaller modules and packages makes it easier to **reuse components and state** across the app without introducing circular dependencies. - Create **individual functions** to encapsulate units of functionality and **reuse them** where needed. # Configuration Source: http://localhost:3000/docs/advanced-onboarding/configuration.md Reflex apps can be configured using a configuration file, environment variables, and command line arguments. ## Configuration File Running `uv run reflex init` will create an `rxconfig.py` file in your root directory. You can pass keyword arguments to the `Config` class to configure your app. For example: ```python # rxconfig.py import reflex as rx config = rx.Config( app_name="my_app_name", # Connect to your own database. db_url="postgresql://user:password@localhost:5432/my_db", # Change the frontend port. frontend_port=3001, ) ``` See the [config reference](https://reflex.dev/docs/api-reference/config/) for all the parameters available. ## Environment Variables You can override the configuration file by setting environment variables. For example, to override the `frontend_port` setting, you can set the `FRONTEND_PORT` environment variable. ```bash FRONTEND_PORT=3001 uv run reflex run ``` ## Command Line Arguments Finally, you can override the configuration file and environment variables by passing command line arguments to `uv run reflex run`. ```bash uv run reflex run --frontend-port 3001 ``` See the [CLI reference](/docs/api-reference/cli) for all the arguments available. ## Customizable App Data Directory The `REFLEX_DIR` environment variable can be set, which allows users to set the location where Reflex writes helper tools like Bun and NodeJS. By default we use Platform specific directories: On windows, `C:/Users//AppData/Local/reflex` is used. On macOS, `~/Library/Application Support/reflex` is used. On linux, `~/.local/share/reflex` is used. # How Reflex Works Source: http://localhost:3000/docs/advanced-onboarding/how-reflex-works.md We'll use the following basic app that displays Github profile images as an example to explain the different parts of the architecture. ```python demo exec import requests import reflex as rx class GithubState(rx.State): url: str = "https://github.com/reflex-dev" profile_image: str = "https://avatars.githubusercontent.com/u/104714959" @rx.event def set_profile(self, username: str): if username == "": return try: github_data = requests.get( f"https://api.github.com/users/{username}" ).json() except: return self.url = github_data["url"] self.profile_image = github_data["avatar_url"] def index(): return rx.hstack( rx.link( rx.avatar(src=GithubState.profile_image), href=GithubState.url, ), rx.input( placeholder="Your Github username", on_blur=GithubState.set_profile, ), ) ``` ## The Reflex Architecture Full-stack web apps are made up of a frontend and a backend. The frontend is the user interface, and is served as a web page that runs on the user's browser. The backend handles the logic and state management (such as databases and APIs), and is run on a server. In traditional web development, these are usually two separate apps, and are often written in different frameworks or languages. For example, you may combine a Flask backend with a React frontend. With this approach, you have to maintain two separate apps and end up writing a lot of boilerplate code to connect the frontend and backend. We wanted to simplify this process in Reflex by defining both the frontend and backend in a single codebase, while using Python for everything. Developers should only worry about their app's logic and not about the low-level implementation details. ### TLDR Under the hood, Reflex apps compile down to a [React](https://react.dev) frontend app and a [FastAPI](https://github.com/tiangolo/fastapi) backend app. Only the UI is compiled to Javascript; all the app logic and state management stays in Python and is run on the server. Reflex uses [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) to send events from the frontend to the backend, and to send state updates from the backend to the frontend. The diagram below provides a detailed overview of how a Reflex app works. We'll go through each part in more detail in the following sections. ```python eval rx.el.a( rx.image( src="https://web.reflex-assets.dev/other/architecture.webp", alt="Reflex app architecture diagram", ), href="https://web.reflex-assets.dev/other/architecture.webp", target="_blank", ) ``` ```python eval rx.box(height="1em") ``` ## Frontend We wanted Reflex apps to look and feel like a traditional web app to the end user, while still being easy to build and maintain for the developer. To do this, we built on top of mature and popular web technologies. When you run `uv run reflex run`, Reflex compiles the frontend down to a single-page [Next.js](https://nextjs.org) app and serves it on a port (by default `3000`) that you can access in your browser. The frontend's job is to reflect the app's state, and send events to the backend when the user interacts with the UI. No actual logic is run on the frontend. ### Components Reflex frontends are built using components that can be composed together to create complex UIs. Instead of using a templating language that mixes HTML and Python, we just use Python functions to define the UI. ```python def index(): return rx.hstack( rx.link( rx.avatar(src=GithubState.profile_image), href=GithubState.url, ), rx.input( placeholder="Your Github username", on_blur=GithubState.set_profile, ), ) ``` In our example app, we have components such as `rx.hstack`, `rx.avatar`, and `rx.input`. These components can have different **props** that affect their appearance and functionality - for example the `rx.input` component has a `placeholder` prop to display the default text. We can make our components respond to user interactions with events such as `on_blur`, which we will discuss more below. Under the hood, these components compile down to React components. For example, the above code compiles down to the following React code: ```jsx ``` Many of our core components are based on [Radix](https://radix-ui.com/), a popular React component library. We also have many other components for graphing, datatables, and more. We chose React because it is a popular library with a huge ecosystem. Our goal isn't to recreate the web ecosystem, but to make it accessible to Python developers. This also lets our users bring their own components if we don't have a component they need. Users can [wrap their own React components](/docs/wrapping-react/overview) and then [publish them](/docs/custom-components/overview) for others to use. Over time we will build out our [third party component ecosystem](/docs/custom-components/overview) so that users can easily find and use components that others have built. ### Styling We wanted to make sure Reflex apps look good out of the box, while still giving developers full control over the appearance of their app. We have a core [theming system](/docs/styling/theming) that lets you set high level styling options such as dark mode and accent color throughout your app to give it a unified look and feel. Beyond this, Reflex components can be styled using the full power of CSS. We leverage the [Emotion](https://emotion.sh/docs/introduction) library to allow "CSS-in-Python" styling, so you can pass any CSS prop as a keyword argument to a component. This includes [responsive props](/docs/styling/responsive) by passing a list of values. ## Backend Now let's look at how we added interactivity to our apps. In Reflex only the frontend compiles to Javascript and runs on the user's browser, while all the state and logic stays in Python and is run on the server. When you run `uv run reflex run`, we start a FastAPI server (by default on port `8000`) that the frontend connects to through a websocket. All the state and logic are defined within a `State` class. ```python class GithubState(rx.State): url: str = "https://github.com/reflex-dev" profile_image: str = "https://avatars.githubusercontent.com/u/104714959" def set_profile(self, username: str): if username == "": return github_data = requests.get(f"https://api.github.com/users/{username}").json() self.url = github_data["url"] self.profile_image = github_data["avatar_url"] ``` The state is made up of **vars** and **event handlers**. Vars are any values in your app that can change over time. They are defined as class attributes on your `State` class, and may be any Python type that can be serialized to JSON. In our example, `url` and `profile_image` are vars. Event handlers are methods in your `State` class that are called when the user interacts with the UI. They are the only way that we can modify the vars in Reflex, and can be called in response to user actions, such as clicking a button or typing in a text box. In our example, `set_profile` is an event handler that updates the `url` and `profile_image` vars. Since event handlers are run on the backend, you can use any Python library within them. In our example, we use the `requests` library to make an API call to Github to get the user's profile image. ## Event Processing Now we get into the interesting part - how we handle events and state updates. Normally when writing web apps, you have to write a lot of boilerplate code to connect the frontend and backend. With Reflex, you don't have to worry about that - we handle the communication between the frontend and backend for you. Developers just have to write their event handler logic, and when the vars are updated the UI is automatically updated. You can refer to the diagram above for a visual representation of the process. Let's walk through it with our Github profile image example. ### Event Triggers The user can interact with the UI in many ways, such as clicking a button, typing in a text box, or hovering over an element. In Reflex, we call these **event triggers**. ```python rx.input( placeholder="Your Github username", on_blur=GithubState.set_profile, ) ``` In our example we bind the `on_blur` event trigger to the `set_profile` event handler. This means that when the user types in the input field and then clicks away, the `set_profile` event handler is called. ### Event Queue On the frontend, we maintain an event queue of all pending events. An event consists of three major pieces of data: - **client token**: Each client (browser tab) has a unique token to identify it. This let's the backend know which state to update. - **event handler**: The event handler to run on the state. - **arguments**: The arguments to pass to the event handler. Let's assume I type my username "picklelo" into the input. In this example, our event would look something like this: ```json { "client_token": "abc123", "event_handler": "GithubState.set_profile", "arguments": ["picklelo"] } ``` On the frontend, we maintain an event queue of all pending events. When an event is triggered, it is added to the queue. We have a `processing` flag to make sure only one event is processed at a time. This ensures that the state is always consistent and there aren't any race conditions with two event handlers modifying the state at the same time. ```md alert info # There are exceptions to this, such as [background events](/docs/events/background-events) which allow you to run events in the background without blocking the UI. ``` Once the event is ready to be processed, it is sent to the backend through a WebSocket connection. ### State Manager Once the event is received, it is processed on the backend. Reflex uses a **state manager** which maintains a mapping between client tokens and their state. By default, the state manager is just an in-memory dictionary, but it can be extended to use a database or cache. In production we use Redis as our state manager. ### Event Handling Once we have the user's state, the next step is to run the event handler with the arguments. ```python def set_profile(self, username: str): if username == "": return github_data = requests.get(f"https://api.github.com/users/{username}").json() self.url = github_data["url"] self.profile_image = github_data["avatar_url"] ``` In our example, the `set_profile` event handler is run on the user's state. This makes an API call to Github to get the user's profile image, and then updates the state's `url` and `profile_image` vars. ### State Updates Every time an event handler returns (or [yields](/docs/events/yield-events)), we save the state in the state manager and send the **state updates** to the frontend to update the UI. To maintain performance as your state grows, internally Reflex keeps track of vars that were updated during the event handler (**dirty vars**). When the event handler is done processing, we find all the dirty vars and create a state update to send to the frontend. In our case, the state update may look something like this: ```json { "url": "https://github.com/picklelo", "profile_image": "https://avatars.githubusercontent.com/u/104714959" } ``` We store the new state in our state manager, and then send the state update to the frontend. The frontend then updates the UI to reflect the new state. In our example, the new Github profile image is displayed. # API Integration Source: http://localhost:3000/docs/ai/apis.md Not every service has a first-class integration — but your app can connect to **any external API** directly. By using standard HTTP requests, you can integrate with virtually any platform, fetch or send data, and trigger workflows without needing a prebuilt connector. This gives you full flexibility to work with modern REST, GraphQL, or other web APIs. ## What You Can Do With custom API calls, your app can: - Connect to **any REST or GraphQL API** on the web. - **Send and receive data** from external services. - Trigger actions like creating records, sending messages, or fetching analytics. - Build **custom automations** and workflows around APIs. - Chain API calls with other integrations or AI actions for powerful flows. ## Step 1: Get API Access 1. Identify the service you want to connect to. 2. Check its developer documentation for API access requirements. 3. Obtain the necessary credentials (e.g., **API key**, **token**, or **OAuth**). 4. Store credentials securely using environment variables — **never** hardcode secrets. *Example:* - **API Key:** `sk-xxxxxxxxxxxxxxxx` - **Base URL:** `https://api.example.com/v1/` ## Step 2: Hook up with your App 1. In the AI Builder navigate to the `secrets` tab and add your API credentials as secrets. 2. Then prompt the AI to use these secrets to do what you want and it will install the necessary libraries and set up the API calls for you. ## Step 3: Notes * **Keep secrets safe:** Use environment variables or secret storage for API keys. * **Check rate limits:** Many APIs have request limits — build accordingly. * **Combine with AI or other integrations:** For example, fetch data via API and summarize it using an LLM. With API integrations, you can connect your app to **almost any modern platform or service**, giving you unlimited extensibility beyond native integrations. # Copy App Source: http://localhost:3000/docs/ai/app-lifecycle/copy-app.md The **Copy** feature lets you duplicate an existing app inside Reflex Build. This is useful when you want to experiment with changes without affecting the original project, or when you want to use an app as a starting point for a new idea. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/app_lifecycle/copy_light.avif", alt="Copying an app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## How to Copy an App 1. In the Reflex Build workspace, click on the arrow down icon next to the deploy button and click on the **Copy** button. You can also do this in the Settings tab. 2. Reflex Build will create a new app in your workspace with the same: - Code files and components - State and configuration - Dependencies The copied app will appear as a separate project, independent from the original. ## Common Use Cases - **Experiment Safely** Try out new components, layouts, or integrations without risking your working app. - **Create Variations** Use the original app as a base to quickly spin up a different version (e.g., a light and dark theme version). - **Template Reuse** Turn an app into a personal template and copy it each time you start a new project. ## Best Practices - Rename your copied app immediately so it’s easy to distinguish from the original. # Deploy App Source: http://localhost:3000/docs/ai/app-lifecycle/deploy-app.md ```python exec import reflex as rx ``` It is easy to deploy your app into production from Reflex Build to Reflex Cloud. Simply click the `Deploy` button in the top right corner of Reflex Build, as shown below: ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/app_lifecycle/deploy_light.avif", alt="Deploying an app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` When deploying you can set the following options: - **App Name**: The name of your app - **Hostname**: Set your url by setting your hostname, i.e. if you set `myapp` as your hostname, your app will be available at `myapp.reflex.run` - **Region**: The regions where your app will be deployed - **VM Size**: The size of the VM where your app will be deployed - **Secrets**: The environment variables that will be set for your app, you can load the variables currently being used by your app by clicking the `Load from settings` button Note: Hostname customization, region selection, and VM sizing are only available on paid plans. # Download App Source: http://localhost:3000/docs/ai/app-lifecycle/download-app.md You can download your Reflex Build project if you want to work on it locally or self-host it outside the AI Builder. **Tip:** The recommended workflow is to use the GitHub integration, which keeps your code version-controlled and in sync. Downloading is useful if GitHub integration isn’t available or you just want a one-time export. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/app_lifecycle/download_light.avif", alt="Downloading an app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## How to Download 1. In the AI Builder workspace, click on the arrow down icon next to the deploy button and click on the **Download** button. You can also do this in the Settings tab. 2. A `.zip` file will be generated containing your entire Reflex app, including: - Source code (`.py` files, components, state, etc.) - `requirements.txt` with dependencies - Config files (`rxconfig.py`, `.env`, etc.) # Fork App Source: http://localhost:3000/docs/ai/app-lifecycle/fork-app.md The **Fork App** feature lets you take an existing app and create your own version of it. This is perfect for **experimenting, customizing, or building on top of someone else’s work** without affecting the original app. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/fork_template_light.avif", alt="Forking an app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## How to Fork an App 1. Browse or open an app you’d like to use as a starting point. 2. Click the **Fork** button in the app’s top right corner. 3. The AI Builder will create a **copy of the app** in your workspace. 4. You can now **edit, customize, and expand** your forked app independently of the original. ## What Happens When You Fork - You get a **full copy** of the original app, including all pages, components, and configurations. - The forked app is **completely separate**, so changes you make do not affect the original. - You can **rename, deploy, or share** your forked app like any other app in your workspace. ## Common Use Cases - **Start From an Example** Use a sample or shared app as a foundation to save time and learn best practices. - **Experiment Safely** Try new ideas or features without risking changes to the original app. - **Collaborate and Customize** Fork a teammate’s app to tailor it to your needs while keeping the original intact. # General App Settings Source: http://localhost:3000/docs/ai/app-lifecycle/general.md The **General App Settings** section lets you manage key aspects of your app, including its name, ID, and deletion. This is your central place to view and update your app’s core information. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/app_lifecycle/general_light.avif", alt="General app settings in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## How to Access Settings 1. In the AI Builder workspace, on the top bar click the more 3 dots icon and then click the **Settings** tab. 2. This will open the **Settings** tab to see your app’s main settings. ## What You Can Do - **Change App Name** Update the name of your app to reflect its purpose or version. - **Change App Visibility** Update the visibility of your app to public or private. - **View App ID** Find the unique identifier for your app, which can be used for integrations or support. - **Fork App** Fork your app to create a copy of it. -- **Download App** Download your app to your local machine. - **Delete App** Permanently remove an app you no longer need. **Warning:** This action cannot be undone. # Share App Source: http://localhost:3000/docs/ai/app-lifecycle/share-app.md The **Share** feature makes it easy to show your app to others without deploying it. When you share, Reflex Build generates a unique link that points to the current version of your project in the builder. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/app_lifecycle/share_light.avif", alt="Sharing an app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## How to Share 1. In the AI Builder workspace, click on the arrow down icon next to the deploy button and click on the **Share** button. 2. A popup will appear with a **shareable link**. 3. Copy the link and send it to teammates, collaborators, or stakeholders. ## What Others See - The link opens a **read-only view** of your app generation. - Recipients can see the app preview but cannot make edits. - This makes it safe to share work-in-progress versions for quick feedback. ## Common Use Cases - **Get Feedback Quickly** Share a work-in-progress version with your team before deploying. - **Demo Features** Send a link to showcase a new component, layout, or integration. - **Collaboration** Share context with another developer before handing off to GitHub or download. # AI Testing Feature Source: http://localhost:3000/docs/ai/features/automated-testing.md ## Overview The Testing feature allows you to automatically test your generated applications for common issues and functionality problems. The AI will analyze your app and identify potential bugs, broken links, navigation issues, and other problems. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src=rx.color_mode_cond( "https://web.reflex-assets.dev/ai_builder/features/test_light.webp", "https://web.reflex-assets.dev/ai_builder/features/test_dark.webp", ), alt="Automated testing in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## How to Use 1. **Start Testing**: Type "test this app" or similar command to activate testing mode 2. **AI Analysis**: The AI will automatically switch to testing mode and begin analyzing your application 3. **Review Results**: The preview tab switches to "Testing" mode to show the testing process and results ## What Gets Tested The AI automatically checks for: - **Broken Navigation**: Links that don't work or lead to missing pages - **Non-functional Buttons**: Buttons that don't respond or trigger errors - **Broken Links**: External or internal links that return errors - **UI/UX Issues**: Interface elements that don't function as expected - **Data Flow Problems**: Issues with forms, inputs, and data handling - **Layout Issues**: Visual or structural problems with the interface ## Testing Interface When testing is active: - The preview tab changes to "Testing" mode - You can see the AI interact with your application in real-time - Issues and results are reported as they're discovered - The testing process is visual and interactive ## Benefits - **Quality Assurance**: Catch issues before deployment - **Time Saving**: Automated testing is faster than manual checking - **Comprehensive Coverage**: Tests multiple aspects of your application # Connecting to Git Providers Source: http://localhost:3000/docs/ai/features/connect-to-git-providers.md --- tags: DevTools description: Connect Reflex Build to GitLab, Bitbucket, or any Git remote using a repository URL and fine-grained access token. --- Besides the [GitHub integration](/docs/ai/features/connect-to-github/), Reflex Build can sync to any Git remote, including GitLab, Bitbucket, a self-hosted server, or an existing GitHub repository. You connect it with a repository URL and a fine-grained access token, then push and pull the same way you would with GitHub. ## What You Need To connect, provide: - **Remote URL**: the HTTPS clone URL of the repository (for example `https://gitlab.com/your-org/your-app.git`). - **Branch**: the branch to sync with. Point at an existing branch, or name a new branch for an empty repository. - **Fine-grained access token**: a token from your provider with permission to read and write the repository. It authenticates pushes and pulls. Unlike the GitHub integration, which each user authorizes with their own account, the token is saved as one of the app's secrets (`GIT_PAT_TOKEN`). Anyone working on the app pushes and pulls with that stored token, so use a token whose repository access is appropriate to share with the app's collaborators. ## How It Works The connection tracks two separate repositories: the internal history Reflex Build keeps for your app, and your external remote. Pushing copies your app's current files to the remote as a commit; pulling copies the remote's latest files back into the editor. The two histories stay independent, so a sync copies the files across, not the individual commits. ## Connecting 1. Open the Git connection dialog in the editor and choose the generic Git option. 2. Enter the remote URL, the branch, and your fine-grained access token. 3. Choose whether to pull the remote's files into the editor immediately after connecting. After connecting, the **Push** and **Pull** actions work the same as with GitHub. ## Connecting to an Existing Repository To bring in an existing repository, enter its remote URL and a token with access to it, then pull its files into the editor. Reflex Build syncs against that branch from then on. If the repository is empty, name a new branch instead of an existing one, and Reflex Build initializes it on the first push. ## Requirements Git integration is available on plans that include the Git connection feature. If your plan does not include it, connecting will prompt you to upgrade. # Connecting to GitHub Source: http://localhost:3000/docs/ai/features/connect-to-github.md --- tags: DevTools description: Connect Reflex Build to GitHub to version your app, sync code locally, and revert to previous checkpoints. --- ```python exec import reflex as rx ``` Connecting your app to GitHub gives it a version history and lets you edit the code locally. Each sync is a commit in an ordinary Git repository. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/connecting_to_github.avif", alt="Connecting a Reflex AI Builder app to GitHub", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), ) ``` The GitHub integration allows you to: - Save your app progress as commits you control - Work on your code locally and push your local changes back to Reflex Build - Pull changes made elsewhere back into the editor - Revert to any previous version of your app ## How the Connection Works Reflex Build connects to GitHub through the `reflex-build` GitHub App using OAuth. The first time you connect, GitHub prompts you to install the app and authorize access. It then links your GitHub account to Reflex Build, which stores an encrypted access token and uses it to create repositories and push commits for you. Pushes, pulls, and reverts use short-lived GitHub App installation tokens that Reflex Build requests as needed. Your personal OAuth token identifies you and refreshes access. Both are encrypted at rest. ## Team and Multi-User Access **Each user connects their own GitHub account.** Connections are stored per user; there is no shared or organization-level token. If one teammate connects GitHub, another teammate who wants to push or pull has to run the same connect flow and authorize the `reflex-build` app with their own account. Commits are attributed to the GitHub user who made them, and each person's access to a repository follows their own GitHub permissions. The GitHub App installation is separate from per-user authorization. An organization owner can install the `reflex-build` app once and choose which repositories it may access, which controls what is reachable from Reflex Build. Each user still authorizes the app once to link their own account before they can push or pull. ## Where Repositories Are Created When you connect a Build app to GitHub, Reflex Build creates a Git repository for it: - If the `reflex-build` app is installed on a **GitHub organization**, the repository is created inside that organization. - Otherwise, the repository is created under your **personal GitHub account**. New repositories use the `main` branch and are private, unless you make them public when connecting. ## Pushing and Pulling Changes The GitHub popover in the editor syncs in both directions: - **Push**: commit the current state of your app and push it to GitHub. - **Pull**: fetch the latest commits from GitHub and update the files in the editor. - **Switch branch**: check out a different branch and sync its files into the editor. You can also clone the repository, edit it locally, and push your changes back. ## GitHub Commit History The commit history is a great way to see the changes that you have made to your app. You can also revert to previous versions of your app from here. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/github_commit_history.avif", alt="GitHub commit history for a Reflex AI Builder app", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), ) ``` Reverting resets your app's files to an earlier commit. ## Other Git Providers To connect a repository hosted somewhere other than GitHub, such as GitLab, Bitbucket, or a self-hosted Git server, use the generic Git connection instead. See [Connecting to Git Providers](/docs/ai/features/connect-to-git-providers/) for details. ## Requirements Git integration is available on plans that include the Git connection feature. If your plan does not include it, connecting will prompt you to upgrade. # App Style Customization Source: http://localhost:3000/docs/ai/features/customization.md ## Overview The App Style feature allows you to customize the visual appearance of your AI-generated applications. You can choose from predefined design themes or create custom styling to match your brand and preferences. ## How to Use 1. **Access the Feature**: Click on the App Style option to open the customization panel 2. **Choose Your Approach**: - **Custom**: Manually configure individual design elements - **Themes**: Select from professionally designed templates ## Custom Styling Options ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src=rx.color_mode_cond( "https://web.reflex-assets.dev/ai_builder/features/style_light.webp", "https://web.reflex-assets.dev/ai_builder/features/style_dark.webp", ), alt="Custom styling options in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` When using Custom mode, you can adjust: - **Primary Color**: Choose your main brand color from 20+ preset options - **Secondary Color**: Select a complementary color for accents - **Typography**: Pick a font family for your app - **Border Radius**: Control how rounded corners appear - **Shadows**: Add depth with shadow effects - **Spacing**: Adjust the spacing between elements ## Themes These options are predefined popular themes you can choose for your app. Below are some of the available themes you can choose from ```python eval rx.el.div( rx.image( src=rx.color_mode_cond( "https://web.reflex-assets.dev/ai_builder/features/theme_light.webp", "https://web.reflex-assets.dev/ai_builder/features/theme_dark.webp", ), alt="Theme customization in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` - **Minimal Design**: Clean, geometric design with no shadows or gradients - **Modern UI**: Sleek, contemporary interface optimized for performance - **Carbon Design**: Enterprise-grade design following IBM's Carbon system - **Material Design**: Google's design language with elevation and semantic colors # Design Systems Source: http://localhost:3000/docs/ai/features/design-systems.md Design Systems give Reflex Build reusable visual guidelines for every app in a project. Define colors, typography, spacing, component styles, and other brand rules once, then let the AI Builder apply them as it creates and updates your apps. ## Open Design Systems You can access Design Systems in two places: - In your project, open **Project Settings** and select **Design Systems** to create and manage the project's design systems. - In the new-app prompt box, open the **Design System** selector to choose an existing design system or create one without leaving the builder. Design systems belong to the project, so every app in that project can reuse them. ## Create a Design System From **Project Settings > Design Systems**, click **Create new**. Enter a name and provide at least one source for the design: - **Guidelines**: Describe the visual direction in plain language. - **Reference website**: Enter a public website URL. Reflex captures a screenshot of the page and uses its visual language as a reference. - **Reference file**: Upload a PDF or image containing a brand guide, style guide, screenshot, or mockup. You can combine written guidelines with a website or uploaded file. Click **Generate** to create and save the design system. The new design system becomes active automatically. For example: ```text Use a warm, editorial style with cream backgrounds, dark green text, serif headings, compact navigation, and orange accents for primary actions. Keep cards flat with thin borders and use generous spacing between sections. ``` ### Supported Reference Files Reflex accepts one reference file up to 20 MB in any of these formats: - PDF - PNG - JPG or JPEG - WebP ## Start From an Example The **Examples** section includes ready-made design systems such as Minimal, Modern, Material, Carbon, Neobrutalism, and Glassmorphism. Select **Add Style** on an example to add it to the project and make it active. Examples are useful as a starting point. After adding one, edit its saved Markdown instructions to match your brand more closely. ## Select the Active Design System Enable **Auto-enable** next to a saved design system to make it active for subsequent AI Builder requests in the project. You can also select or create a design system from the new-app prompt box before generating an app. Only one design system can be active at a time. Activating, creating, or adding another design system automatically deactivates the current one. Turn off the active design system if you want to build without design-system guidance. The active design system also guides later updates to the project's apps. You can still provide page-specific requirements in your prompt: ```text Use the active design system, but make this checkout page more compact and reserve the accent color for the final purchase action. ``` ## Edit or Delete a Design System In **Project Settings > Design Systems**, open the menu next to a saved design system. - Select **Edit** to change its name or update the Markdown instructions that the AI Builder follows. - Select **Delete** to remove it from the project. Editing the Markdown is useful when you need exact tokens or component rules: ```markdown ## Buttons - Primary buttons use `#0F62FE` with white text. - Use a 4 px corner radius. - Button labels use 14 px semibold text. - Destructive actions use `#DA1E28`. ``` ## Best Practices - Give each design system a descriptive name, such as `Marketing Site` or `Internal Admin Tools`. - Describe concrete choices such as colors, typefaces, spacing, borders, and component behavior instead of asking for a design that is only "modern" or "clean." - Use a public, visually representative page when supplying a reference website. - Upload a focused brand guide or screenshot that clearly shows the styles you want the AI Builder to reproduce. - Review the saved Markdown and add any non-negotiable accessibility or brand requirements. - Keep one design system active while building a consistent group of apps, and switch it when the project needs a different visual direction. # Editor Modes Source: http://localhost:3000/docs/ai/features/editor-modes.md The AI Builder includes a powerful dual-mode editor that lets you view and edit your application code while tracking changes made by the AI. You can seamlessly switch between **Editor Mode** for manual code editing and **Diff Mode** for reviewing AI-generated changes. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/features/diff_light.avif", alt="Reflex AI Builder editor diff view", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## Modes: Editor vs Diff ### Editor Mode The standard code editor where you can: - **Write and modify code** directly in the interface - **Navigate through files** using the file tree - **Make manual changes** to your application - **Save your modifications** which persist across sessions ### Diff Mode A specialized view that highlights changes from the last AI prompt: - **Green highlights** show code additions made by the AI - **Red highlights** show code deletions made by the AI - **Side-by-side comparison** of what changed - **Line-by-line tracking** of modifications ## Switching Between Modes ### Toggle Controls Located in the editor toolbar, you'll find: - **Editor** button - Switch to normal editing mode - **Diff** button - Switch to change tracking mode ### When to Use Each Mode - **Use Editor Mode when:** - Making manual code changes - Writing new functionality - Debugging or fixing issues - General code development - **Use Diff Mode when:** - Reviewing what the AI changed after a prompt - Understanding modifications before accepting them - Tracking the impact of AI suggestions - Learning from AI-generated code patterns ## Understanding Diff Visualizations ### Code Highlighting **Additions (Green):** - New code lines added by the AI - New functions, components, or logic - Enhanced features and improvements **Deletions (Red):** - Code removed by the AI - Replaced or refactored sections - Deprecated functionality ### File Tree Indicators The file tree shows change statistics for each modified file: **Change Indicators:** - **`+5`** - 5 lines added to this file - **`-3`** - 3 lines removed from this file - **`+12 -8`** - 12 lines added, 8 lines removed - **No indicator** - File unchanged **Visual Cues:** - **Green `+` symbol** indicates files with additions - **Red `-` symbol** indicates files with deletions # File Tree Source: http://localhost:3000/docs/ai/features/file-tree.md The **File Tree** in Reflex Build lets you **view, organize, and manage all files and folders** in your project directly from the browser. It’s your central hub for navigating your app’s structure. ## Key Features ### Creating Folders - Click the **New Folder** button to create a folder inside the currently selected directory. - Enter a name and press **Enter** to confirm. - Folders can be nested to organize your project hierarchically. ### Creating Files - Click the **New File** button to create a file inside the current folder. - Enter a file name and extension (e.g., `main.py`) and press **Enter**. - Files are immediately visible in the tree and ready for editing in the code editor. ### Renaming Files or Folders - Right-click on a file or folder and select **Rename**. - Type the new name and press **Enter** to confirm. - Renaming automatically updates references in your project where applicable. ### Deleting Files or Folders - Right-click on a file or folder and select **Delete**. - Deleted items are permanently removed, so be careful when deleting important files. ### Drag-and-Drop from System - Drag files or folders from your computer directly into the File Tree. - The editor will automatically import them into the selected folder. - This works for individual files or entire folder structures, making adding assets or scripts quick and easy. # Reflex Build IDE Source: http://localhost:3000/docs/ai/features/ide.md Reflex Build includes a **powerful, in-browser IDE** built on **Monaco Editor**, designed to make coding fast, efficient, and enjoyable—all without leaving your browser. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.video( src="https://www.youtube.com/embed/UAj9vUweQ5g", width="100%", height="400px", ), class_name="p-1 my-4 rounded-lg bg-secondary-5", ) ``` ## Features ### Real-Time Editing Edit your code and see changes reflected immediately in your project. No manual saves or rebuilds—stay focused and iterate faster. ### Syntax Highlighting & Error Detection Write code confidently with **syntax highlighting**, **inline error alerts**, and **linting**. Catch issues as you type, reducing bugs and speeding up development. ### Code Snippets & Autocomplete Speed up development with **autocomplete** for functions, variables, and imports, as well as reusable **code snippets** for common patterns. ### Integrated Preview Quickly preview your changes directly in the editor without switching contexts, ensuring your app behaves as expected as you code. ### Built-In Terminal for Debugging Use the integrated terminal to run commands, debug issues, and inspect logs—all within the IDE. No need to switch tools or tabs. # Use Images as a prompt Source: http://localhost:3000/docs/ai/features/image-as-prompt.md ```python exec import reflex as rx ``` Uploading an image (screenshot) of a website (web) app of what you are looking to build gives the AI really good context. *This is the recommended way to start an app generation.* Below is an image showing how to upload an image to the AI Builder, you can click on the "Attach" button to upload an image, drag and drop an image, or paste an image from the clipboard: ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/image_upload.avif", alt="Using an image as a prompt in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` The advised prompt to use is: `Build an app from a reference image` # Installing External Packages Source: http://localhost:3000/docs/ai/features/installing-external-packages.md ```python exec import reflex as rx ``` Reflex Build allows you to install external python packages to use in your app. This is useful if you want to use a package that is not included in the default Reflex Build environment. Examples might include `openai`, `langsmith`, `requests`, etc. There are two ways to install external packages: 1. **Through the Chat Interface**: You can ask the AI to install a package for you. 2. **Add to the `requirements.txt` file**: You can add the package to the `requirements.txt` file and then save the app. This will install the package in your app's environment. ## Installing through the Chat Interface Enter the name of the package you want to install in the chat interface. The AI will then install the package for you. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/external_packages_input.avif", alt="Installing external packages via the chat interface", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## Installing through the requirements.txt file Add the package to the `requirements.txt` file and then save the app. This will install the package in your app's environment and recompile your app. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/external_packages_requirements.avif", alt="Installing external packages via requirements.txt", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` # Integrations Shortcut Source: http://localhost:3000/docs/ai/features/integration-shortcut.md Reflex Build supports powerful integrations like databases, OpenAI, and Databricks, allowing you to connect external services to your app without complex setup. These integrations help you add advanced functionality—like AI-powered features, data analytics, or persistent storage—while speeding up development. The **Add Integrations** button makes it easy to connect external services to your app while chatting with the AI Builder. A panel with a list of available integrations will appear, you can quickly add integrations that your app can reference and connect to. Once in your app, you can access your integrations by clicking the flow or cog icon in the bottom left inside the chat area. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src=rx.color_mode_cond( "https://web.reflex-assets.dev/ai_builder/features/shortcut_light.webp", "https://web.reflex-assets.dev/ai_builder/features/shortcut_dark.webp", ), alt="Integrations shortcut in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## How to Use 1. In the AI Builder home, click the **Add Integrations** button. And if you're already in an app, click the flow or cog icon in the bottom left inside the chat area. 2. A list of available integrations will appear (e.g. Database, Databricks, OpenAI, etc.). 3. Click the Add button next to an integration to select it. 4. The integration will be added to your app and becomes available for connection. Then you can fill the required fields for the integration. 5. The AI Builder now knows your app has access to this integration and can generate code that uses it. ## What It Does - **Quick Access** – Easily browse and select from available integrations. - **Automatic Connection** – Integrations are added to your app and become available for the AI to use in generated code. - **No Manual Setup** – Skip complex configuration—the AI Builder handles the integration setup for you. ## Common Use Cases - **Database Queries** Show me the top 10 users ordered by signup date from my database. - **AI Features** Create a chat application that uses OpenAI to generate responses. # Knowledge Source: http://localhost:3000/docs/ai/features/knowledge.md The **Knowledge** feature lets you add context or rules that the AI Builder can reference when generating apps. This ensures your apps follow your guidelines, standards, or specific business logic. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/features/knowledge_light.avif", alt="Reflex AI Builder knowledge section", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## How to Add Knowledge 1. In the AI Builder top bar, click the more 3 dots icon and then click the **`Knowledge`** tab. 2. Enter your rule, guideline, or context description. 3. Save the entry. The AI Builder will automatically use it when generating apps. ## How It Works - The AI Builder references your knowledge entries as rules or guidelines. - Rules can define naming conventions, component usage, layout preferences, or other custom logic. - Multiple rules can be added to cover different aspects of app generation. ## Common Use Cases - **Maintain Consistency** Ensure all generated apps follow your company’s design or naming standards. - **Enforce Business Logic** Guide the AI Builder to follow specific workflows, validations, or feature requirements. - **Quickly Adapt AI Behavior** Add or update rules to influence new app generations without manual edits. # Restore Checkpoint Source: http://localhost:3000/docs/ai/features/restore-checkpoint.md The **Restore Checkpoint** feature allows you to roll back your app to any previous state during your AI Builder conversation. This is useful when you want to undo recent changes and return to an earlier version of your app. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/features/restore_light.avif", alt="Restoring a checkpoint in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## How It Works Every time the AI agent makes changes to your app, a checkpoint is automatically created. You can restore to any of these checkpoints at any time, effectively undoing all changes made after that point. ## Using Restore Checkpoint 1. **Locate the Restore Icon**: At the end of each AI agent message that made changes to your app, you'll see a circular arrow icon (↻). 2. **Click to Restore**: Click the circular arrow icon next to the message you want to restore to. 3. **Confirm the Action**: The app will restore to the exact state it was in after that specific message was processed. 4. **Continue Building**: After restoring, you can continue the conversation and make new changes from that point. ## When to Use Restore Checkpoint - **Undo Unwanted Changes**: When the AI made changes you don't like - **Try Different Approaches**: Restore and ask the AI to implement a feature differently - **Fix Broken Functionality**: Roll back when new changes break existing features - **Experiment Safely**: Test different solutions knowing you can always restore to a checkpoint ## Important Notes - Restoring will **permanently delete** all changes made after the selected message - You cannot undo a restore operation - choose your restore point carefully - The conversation history remains intact, but code changes after the restore point are lost - Restore checkpoint only affects your current building session > **Tip:** Before making major changes, note which message represents your last stable checkpoint so you can easily restore if needed. # Secrets Source: http://localhost:3000/docs/ai/features/secrets.md The **Secrets** feature allows you to securely store environment-specific values that your app can use, such as API keys, tokens, or other sensitive information. ```python exec import reflex as rx def render_image(): return rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/features/secrets_light.avif", alt="Adding secrets in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ```python eval rx.el.div(render_image()) ``` ## Adding Secrets ### 1. Add Individually - **Description:** Set a single secret by providing a key and value. - **Example:** - Key: `OPENAI_API_KEY` - Value: `sk-xxxxxx` - **Behavior:** The secret is encrypted and accessible to your app at runtime. ### 2. Add in Bulk (Raw Editor) - **Description:** Upload multiple secrets at once using a simple `VAR=VALUE` format. - **Example:** ```text DATABASE_URL=postgresql://user:pass@host:5432/db STRIPE_SECRET_KEY=sk_test_xxxxx OPENAI_API_KEY=sk-xxxxxx ``` ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/features/secret_bulk_light.avif", alt="Adding secrets in bulk with the raw editor", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` - **Behavior:** Each secret is securely stored and immediately available in the app environment. # Figma Integration Source: http://localhost:3000/docs/ai/figma.md Integration Coming Soon! # Files Source: http://localhost:3000/docs/ai/files.md To upload a file to the AI Builder click the `📎 Attach` button and select the file you want to upload from your computer. You can also drag and drop files directly into the chat window. This section does not cover uploading images. Check out [Images](/docs/ai/images/) to learn more about uploading images. ```md alert ## Supported File Types The AI Builder currently supports the following file types for upload and processing: 1. `.pdf` 2. `.doc` 3. `.docx` 4. `.xls` 5. `.xlsx` 6. `.ppt` 7. `.pptx` 8. `.odt` 9. `.ods` 10. `.odp` 11. `.rtf` 12. `.csv` 13. `.txt` 14. `.md` 15. `.markdown` 16. `.json` 17. `.xml` 18. `.yaml` 19. `.yml` 20. `.tsv` ``` The files you upload will automatically be added to the `assets/` folder of your app, and the AI Builder will be able to read and process their contents as part of your prompts. The maximum number of files you can upload at a time is `5`. The maximum file size for uploads is `5MB`. If you need to work with larger files, consider breaking them into smaller chunks or using external storage solutions and linking to them via APIs. # Images Source: http://localhost:3000/docs/ai/images.md ## Upload an image as context for AI Builder To upload an image to the AI Builder that will be used as context for the AI builder to build an app that is similar to the image click the `📎 Attach` button and select the file you want to upload from your computer. You can also drag and drop files directly into the chat window. ```md alert ## Supported Image Types The AI Builder currently supports the following image types for upload and processing: 1. `.png` 2. `.jpg` 3. `.jpeg` 4. `.webp` 5. `.gif` 6. `.svg` 7. `.bmp` 8. `.tiff` 9. `.tif` 10. `.ico` ``` ## Upload an image to be used within the app If you want to upload an image to be used within the app, such as a company logo, then you can manually upload it to the `assets/` folder within the `code` tab. Drag and drop the image into the `assets/` folder. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/add_images_to_assets.avif", alt="Uploading an image to app assets in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` Video uploads are not currently supported but are coming soon! # AGENTS.md and CLAUDE.md Source: http://localhost:3000/docs/ai/integrations/agents-md.md `AGENTS.md` and `CLAUDE.md` are project-level instruction files that AI coding assistants read when they enter your repository. They give the assistant durable, repository-specific context so it follows Reflex conventions instead of generic defaults. - `AGENTS.md` is read by agents that follow the [AGENTS.md convention](https://agents.md), including Cursor, OpenCode, OpenAI Codex, and Pi. - `CLAUDE.md` is read by [Claude Code](https://code.claude.com/docs/en/memory). Claude Code does not read `AGENTS.md` directly — see [Sharing With Claude Code](#sharing-with-claude-code) below. A Reflex project should have at least one of these files at the project root, next to `rxconfig.py`. ## Recommended Content The [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository ships an `AGENTS.md` template that points assistants at the [Reflex Agent Skills](/docs/ai/integrations/skills/) for environment setup, documentation lookup, and process management. Use it as the starting point, then add anything specific to your codebase. ```md alert info # `AGENTS.md` references skills by name, so it works once the [Reflex Agent Skills](/docs/ai/integrations/skills/) are installed in the assistant. ``` ## Installation `reflex init` writes a starter `AGENTS.md` into the project root alongside `rxconfig.py` by default (pass `--no-agents` to opt out): ```bash reflex init ``` The Reflex-provided content sits between `reflex managed` begin/end markers. Anything you add outside the markers is preserved when init refreshes the managed section, so it is safe to re-run. If a pre-existing `AGENTS.md` has no markers, the managed section is added at the top and your content is kept below it. For Claude Code users, init also creates a `CLAUDE.md` importing `AGENTS.md` (or updates an existing one that doesn't reference it). To add the template to an existing project, download it into the project root, next to `rxconfig.py`: ```bash curl -fsSL https://raw.githubusercontent.com/reflex-dev/agent-skills/main/AGENTS.md -o AGENTS.md ``` Or copy it manually from a local clone of the [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository. ## Sharing With Claude Code Claude Code reads `CLAUDE.md`, not `AGENTS.md`. To avoid duplicating content, create a `CLAUDE.md` that [imports](https://code.claude.com/docs/en/memory#import-additional-files) `AGENTS.md` using the `@` syntax: ```md @AGENTS.md ## Claude Code Add any Claude-specific instructions here. ``` Claude Code expands the `@AGENTS.md` import at session start, then appends anything you write below it. Both files stay in sync from a single source. After installation, your project root looks like: ```text my_app/ AGENTS.md CLAUDE.md rxconfig.py my_app/ my_app.py ``` ## Project-Specific Additions The template covers Reflex-wide setup. Below it, add anything else the assistant should know about your project: - Internal conventions and code style. - Required lint, type-check, or test commands. - Folder layout and where new code should go. - Hosting or deployment notes. Keep entries short and imperative — assistants follow concise, direct instructions more reliably than long paragraphs. ## Keeping Files Updated Reflex evolves quickly. If `reflex init` created your `AGENTS.md`, re-running `reflex init` refreshes the content between the managed markers while preserving everything you added outside them. If you used `curl` to download the template, re-run the same command to refresh it (note this replaces the whole file): ```bash curl -fsSL https://raw.githubusercontent.com/reflex-dev/agent-skills/main/AGENTS.md -o AGENTS.md ``` If you cloned the [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository, pull the latest changes and copy the file back into your project: ```bash cd agent-skills git pull cp AGENTS.md /path/to/your/reflex-project/AGENTS.md ``` ## Combining With Skills and MCP `AGENTS.md` and `CLAUDE.md` anchor the assistant in your project. Pair them with the other onboarding tools for deeper Reflex knowledge: - [Reflex Agent Skills](/docs/ai/integrations/skills/) provide reusable workflows that the file references by name. - [Reflex MCP](/docs/ai/integrations/mcp-overview/) provides structured documentation lookup at runtime. - The [llms.txt index](/llms.txt) gives a broad map of the documentation in one file. # AI Onboarding Source: http://localhost:3000/docs/ai/integrations/ai-onboarding.md ```python exec import reflex as rx def _resource_card( title: str, body: str, href: str, action: str, target: str = "_self" ) -> rx.Component: return rx.el.a( rx.el.div( rx.el.h3(title, class_name="text-base font-semibold text-secondary-12"), rx.el.p(body, class_name="text-sm leading-6 text-secondary-11"), rx.el.div(action, class_name="text-sm font-semibold text-primary-10"), class_name="flex h-full flex-col gap-2 rounded-lg border border-secondary-a4 bg-white-1 p-4 transition-colors hover:bg-secondary-2 shadow-xs", ), href=href, target=target, class_name="no-underline", ) def onboarding_resources() -> rx.Component: return rx.el.div( _resource_card( "Docs for Agents", "Give your agent current Reflex documentation as Markdown, llms.txt, or structured MCP context.", "/llms.txt", "Open llms.txt", target="_blank", ), _resource_card( "MCP", "Connect supported AI tools to Reflex documentation and component information through MCP.", "/ai/integrations/mcp-overview/", "View MCP overview", ), _resource_card( "Skills", "Install Reflex Agent Skills so assistants follow Reflex-specific workflows for docs, setup, and process management.", "/ai/integrations/skills/", "Install skills", ), _resource_card( "Reflex Build", "Use Reflex Build when you want an AI-native environment for creating, editing, previewing, and shipping apps.", "/ai/overview/best-practices/", "Read best practices", ), class_name="grid grid-cols-1 gap-3 md:grid-cols-2 my-6", ) ``` Everything you need to onboard your AI coding assistant to Reflex. If you are building Reflex apps with AI, combine current docs, structured MCP context, and local skills so the assistant can plan, code, run, and debug with the same assumptions as the Reflex docs. ```python eval onboarding_resources() ``` ## Prerequisite: Choose Your Workflow You do not need an API key to read Reflex documentation. Start by deciding how your assistant will work with Reflex: - For local app development, use Python 3.10 or newer and a project virtual environment. - For current documentation context, give the assistant Markdown docs or `llms.txt`. - For structured tool access, use the Reflex MCP integration. - For repeatable agent behavior, install Reflex Agent Skills. - For a browser-based AI builder, use Reflex Build. ## Reflex Docs for Agents You can give your assistant current Reflex documentation in a few ways. `````md tabs ## Markdown Pages Every docs page has a Markdown version that agents can read directly. Add `.md` to the docs path: ```text https://reflex.dev/docs/ai/integrations/ai-onboarding.md ``` Use this when an agent needs one focused page. ## llms.txt Use the generated docs index when an agent needs a broad map of Reflex docs: ```text https://reflex.dev/docs/llms.txt ``` The index groups docs by section and links to agent-friendly Markdown assets. ## MCP Use MCP when your editor or agent can call tools for structured documentation and component lookup: ```text https://build.reflex.dev/mcp ``` See the [MCP overview](/docs/ai/integrations/mcp-overview/) and [MCP installation](/docs/ai/integrations/mcp-installation/) guides for details. ````` ## Reflex MCP The Reflex MCP integration gives supported AI tools structured access to Reflex framework docs and component information. Use it when your assistant can connect to an MCP server and benefit from tool-assisted lookup while editing code. ```md alert warning # The Reflex MCP integration is currently only available for enterprise customers. Please [book a demo](https://reflex.dev/pricing/) to discuss access. ``` ## Reflex Agent Skills Reflex Agent Skills are local instruction packs that teach AI assistants how to work with Reflex projects. They cover: - Current Reflex docs and concept lookup. - Python environment setup for Reflex projects. - Reflex compile, run, restart, and debugging workflows. Install the skill pack from the [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository, or follow the [Skills installation guide](/docs/ai/integrations/skills/). ```bash npx skills add reflex-dev/agent-skills ``` ## Quick Start Prompts Use these prompts to give your agent a strong starting point. `````md tabs ## New App ```text Create a new Reflex app. Use current Reflex documentation, set up a Python 3.10+ virtual environment, initialize the project, and validate it with reflex compile --dry before handing it back. ``` ## Existing App ```text Work on this existing Reflex app. First inspect the project structure and current dependencies. Use Reflex docs for current APIs, make the requested change, then run reflex compile --dry or the project's existing test command. ``` ## Debugging ```text Debug this Reflex app. Read the error and relevant logs, identify whether the failure is in imports, state, event handlers, routing, components, or runtime setup, then apply the smallest fix and re-run validation. ``` ````` ## Reflex Build Reflex Build is the AI-native way to create Reflex apps in the browser. Use it when you want to generate, edit, preview, and share apps without setting up a local environment first. Start with the [Reflex Build best practices](/docs/ai/overview/best-practices/) guide, then use MCP and Skills when you want your local assistant to keep working with the same Reflex concepts. ## Recommended Validation Ask your assistant to validate Reflex changes before it hands work back: ```bash reflex compile --dry ``` For running apps, pair that with the process-management workflow from Reflex Agent Skills so the assistant restarts only the server process it owns. # Airtable Integration Source: http://localhost:3000/docs/ai/integrations/airtable.md --- tags: Data Infrastructure description: Connect your apps to Airtable to read, create, and update records in real time. --- The **Airtable Integration** lets your apps interact with Airtable bases to **store, fetch, and update structured data** directly from workflows, user actions, or automated triggers. Perfect for managing dynamic content, customer data, or workflows without complex backend setup. ## What You Can Do With the Airtable Integration, your app can: * Read and display data from Airtable bases * Create new records dynamically from user actions * Update or delete records through workflows * Power AI experiences with live database content * Sync structured data without hosting your own database ## Step 1: Get Your Airtable API Key 1. Go to [Airtable](https://airtable.com/). 2. Log in or create a free account. 3. Navigate to [**Developer Hub**](https://airtable.com/create/tokens) from your account menu. 4. Under **Personal access tokens**, click **Create token**. 5. Add the appropriate scopes (e.g. `data.records:read` and `data.records:write`). 6. Copy your API token. * Example: `patxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Click the **Settings** gear icon and navigate to the **Integrations** tab. 2. Find and enable the **Airtable** integration. 3. Paste your **API Key** in the input field. 4. Click **Connect** to confirm the connection. ## Step 3: Use in Your Workflows Once connected, you can use Airtable actions in your app to: * Fetch records to display dynamic lists * Add user submissions as new rows * Update status fields or metrics from AI workflows and more. # Anthropic Integration Source: http://localhost:3000/docs/ai/integrations/anthropic.md --- tags: AI description: Connect your apps to Anthropic's Claude models for advanced AI capabilities. --- The **Anthropic Integration** allows your app to use [Anthropic’s Claude models](https://www.anthropic.com/claude) for tasks such as text generation, summarization, reasoning, and other advanced AI capabilities. Once connected, you can call Claude directly from your workflows, UI actions, or automated triggers. ## Step 1: Obtain an Anthropic API Key 1. Go to the [Anthropic Console](https://console.anthropic.com/). 2. Navigate to **API Keys** in your account settings. 3. Click **Create Key** and give it a descriptive name (e.g., “AI Builder”). 4. Copy the generated key. * **Example:** `sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Anthropic** in your app settings. 2. Paste your **Anthropic API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can use Claude for AI-powered features across workflows and components. ## Step 3: Notes * **Keep your key secure:** Do not hardcode your Anthropic API key in public code repositories. * **Use environment-specific keys:** Separate dev, staging, and production keys help manage access and security. * **Secure API access:** The key allows your app to interact with Anthropic endpoints securely and efficiently. # AWS Integration Source: http://localhost:3000/docs/ai/integrations/aws.md --- tags: Data Infrastructure description: Connect to Amazon Web Services (AWS) for cloud storage, databases, compute, AI/ML, and more. --- The **AWS Integration** allows your app to connect to [Amazon Web Services (AWS)](https://aws.amazon.com/) — the world's most comprehensive cloud platform. Once connected, your app can access 200+ AWS services including storage, databases, compute, AI/ML, and more using the boto3 SDK. ## What You Can Do With AWS integration, your app can: - **S3 (Object Storage):** Upload, download, and manage files in S3 buckets. Generate presigned URLs for secure file access. Works with S3-compatible services like MinIO, Cloudflare R2, and DigitalOcean Spaces. - **DynamoDB (NoSQL Database):** Store and query data in a fully managed, serverless NoSQL database with single-digit millisecond performance. - **Lambda (Serverless Functions):** Invoke serverless functions, run code without managing servers, and build event-driven applications. - **SES (Email Service):** Send transactional emails, marketing emails, and notifications with high deliverability. - **SNS (Notifications):** Send push notifications, SMS, and email messages to users across multiple channels. - **SQS (Message Queues):** Build distributed, decoupled systems with reliable message queuing. - **Rekognition (Computer Vision):** Analyze images and videos for object detection, facial analysis, text extraction, and content moderation. - **Comprehend (NLP):** Extract insights from text using natural language processing for sentiment analysis, entity recognition, and topic modeling. - **Bedrock (AI Models):** Access foundation models from AI21 Labs, Anthropic, Cohere, Meta, Stability AI, and Amazon for generative AI applications. - **And 200+ more services:** EC2, RDS, CloudWatch, Polly, Textract, Translate, and many others. ## Step 1: Obtain AWS Credentials 1. Log in to the [AWS Console](https://console.aws.amazon.com/). 2. **Create IAM credentials:** - Navigate to **IAM (Identity and Access Management)**. - Click **Users** → **Add user** or select an existing user. - Attach policies based on the AWS services you plan to use: - **S3:** `AmazonS3FullAccess` or custom S3 policy - **DynamoDB:** `AmazonDynamoDBFullAccess` or custom DynamoDB policy - **Lambda:** `AWSLambdaFullAccess` or custom Lambda policy - **SES:** `AmazonSESFullAccess` or custom SES policy - **Multiple services:** Attach multiple policies or create a custom policy combining permissions **Example custom policy for S3 + DynamoDB:** ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket", "dynamodb:PutItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME/*", "arn:aws:dynamodb:YOUR_REGION:YOUR_ACCOUNT_ID:table/*" ] } ] } ``` 3. **Generate access keys:** - Under **Security credentials**, click **Create access key**. - Choose **Application running outside AWS** as the use case. - Copy your **Access Key ID** and **Secret Access Key**. **Example credentials:** * **Access Key ID:** `AKIAIOSFODNN7EXAMPLE` * **Secret Access Key:** `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add AWS** in your app settings. 2. Enter your **AWS Access Key ID** and **AWS Secret Access Key**. 3. **(Optional)** Select your **AWS Region** (e.g., `us-east-1`, `eu-west-1`). Defaults to `us-east-1`. 4. **(Optional)** Enter an **AWS Endpoint URL** if using S3-compatible services: - **MinIO:** `http://localhost:9000` or your MinIO server URL - **Cloudflare R2:** `https://.r2.cloudflarestorage.com` - **DigitalOcean Spaces:** `https://.digitaloceanspaces.com` - Leave blank for standard AWS services. 5. Click **Connect** to validate and save your integration. Once connected, your app can access AWS services using boto3. ## Important Notes * **IAM permissions:** Ensure your IAM user has the necessary permissions for the AWS services you plan to use. Attach service-specific policies (e.g., `AmazonS3FullAccess`, `AmazonDynamoDBFullAccess`, `AWSLambdaFullAccess`) or create custom policies with least-privilege access. * **AWS Region:** Most AWS services are region-specific. Select the correct region where your resources are located (e.g., S3 buckets, DynamoDB tables, Lambda functions). * **Cost management:** Monitor AWS usage and costs through the AWS Billing Dashboard. Set up billing alerts to avoid unexpected charges. * **Security:** Rotate your access keys regularly and enable MFA for AWS console access. Apply least-privilege IAM policies to limit permissions to only what's needed. * **S3-compatible services:** For S3 operations, this integration works with S3-compatible storage like MinIO (self-hosted), Cloudflare R2 (zero egress fees), and DigitalOcean Spaces. Just provide the custom endpoint URL in the configuration. * **Service availability:** Not all AWS services are available in all regions. Check the [AWS Regional Services List](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/) for service availability. # Azure Auth Manager Integration Source: http://localhost:3000/docs/ai/integrations/azure-auth.md --- tags: Authentication description: Integrate Azure for secure authentication and access management within your application. --- The **Azure Auth Manager Integration** allows your app to authenticate users through Microsoft Azure Active Directory (Azure AD). This integration provides secure OAuth 2.0 authentication and supports multi-tenant applications with customizable tenant access. ## Step 1: Set Up Azure App Registration Before connecting, you need to register your app in Azure Portal: 1 - Go to [Azure Portal](https://portal.azure.com) → **App Registrations** 2 - Click **New registration** as shown in the image below: ![Azure App Registration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/azure_auth_1.webp) 3 - Register your app. Ensure that for the Redirect URI you select **Web** and enter the following URI that you find in the Azure Auth Manager integration settings in AI Builder: ``` https://{your-sandbox}/authorization-code/callback ``` ![Azure App Registration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/azure_auth_2.webp) 4 - On the next page get your `client_id` (`AZURE_CLIENT_ID`) and `tenant_id` (`AZURE_VALID_TENANT_IDS`) from the **Overview** tab. ![Azure App Registration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/azure_auth_3.webp) 5 - Next click `Add a certificate or secret` and copy the generated secret value (`AZURE_CLIENT_SECRET`). ![Azure App Registration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/azure_auth_4.webp) ## Step 2: Configure the Integration 1. Go to the **Integrations** section in your app settings by clicking **`@`** and then clicking the **Integrations** tab at the top. 2. Click **Add** next to Azure Auth Manager. 3. Fill in the credential fields: - Enter your Azure Client ID - Enter your Azure Client Secret - Enter valid tenant IDs (comma-separated for multiple tenants) 4. Click **Connect** to save the integration. Your app can now authenticate users through Azure AD with secure OAuth 2.0 flow. # Cartesia Integration Source: http://localhost:3000/docs/ai/integrations/cartesia.md --- tags: AI description: Add Cartesia’s powerful text-to-speech capabilities for realistic, real-time voice output and audio experiences. --- The **Cartesia Integration** allows your app to generate **high-quality, realistic speech from text** using Cartesia’s advanced voice models. Once connected, you can easily add real-time voice capabilities to workflows, user interactions, and automated experiences. ## Step 1: Obtain a Cartesia API Key 1. Go to the [Cartesia Console](https://www.cartesia.ai/). 2. Log in or create an account. 3. Navigate to **API Keys** in your account settings. 4. Click **New**, give it a descriptive name (e.g., “AI Builder”), and copy it. - Example: `sk_car_xxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Open your app and go to **Integrations → Add Cartesia**. 2. Paste your **Cartesia API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can convert text to speech seamlessly in real time or through workflows. ## Step 3: Notes - **Secure API Access:** Keep your Cartesia API key safe. Do not hardcode it in public code. - **Environment isolation:** Use different keys for dev, staging, and production to ensure clean separation. - **Low latency:** Cartesia supports real-time streaming for conversational experiences. - **Usage management:** Monitor your Cartesia dashboard for quotas, billing, and usage insights. # Cohere Integration Source: http://localhost:3000/docs/ai/integrations/cohere.md --- tags: AI description: Add Cohere’s language models for text generation, classification, retrieval, embeddings, and more. --- The **Cohere Integration** allows your app to use [Cohere’s](https://cohere.com) state-of-the-art language models for natural language understanding and generation. Once connected, your app can power search, summarization, embeddings, classification, and conversational AI features directly from workflows and triggers. ## Step 1: Obtain a Cohere API Key 1. Go to the [Cohere Dashboard](https://dashboard.cohere.com/). 2. Log in or create an account. 3. Navigate to **API Keys** in your account settings. 4. Click **Create Key**, give it a descriptive name (e.g., “AI Builder”), and copy the key. ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Cohere** in your app settings. 2. Paste your **Cohere API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can access Cohere’s endpoints for language tasks like embeddings, classification, chat, and more. ## Step 3: Notes - **Secure API Access:** Keep your Cohere API key private — do not hardcode it in public repositories. - **Environment separation:** Use different keys for development, staging, and production environments. - **Performance:** Cohere’s models support fast inference for production-ready experiences. - **Observability:** Use the Cohere dashboard to monitor usage, quotas, and request logs. # Database Integration Source: http://localhost:3000/docs/ai/integrations/database.md --- tags: Data Infrastructure description: Connect to SQL or NoSQL databases to query, store, and manage structured data. --- The Database Integration allows you to connect your AI-generated applications to real databases, automatically generating schemas and enabling data-driven functionality. ## Supported Databases - **PostgreSQL** - Recommended for production applications - **MySQL** - Popular open-source database - **SQLite** - Lightweight database, perfect for development and small applications - **MSSQL** - Microsoft SQL Server support ## Getting Started ### Opening the Database Integration 1. Navigate to your app in the AI Builder 2. Open the **Settings drawer** (gear icon) 3. Click on the **Integrations** tab 4. Find and enable the **Database** integration ### Connection Methods The Database Integration offers two convenient ways to connect: #### 1. Connection Details (Recommended) This user-friendly form breaks down your database connection into individual fields: **For PostgreSQL, MySQL and MSSQL:** - **Database Type**: Select from dropdown (PostgreSQL/MySQL/MSSQL) - **Hostname**: Your database server address (e.g., `localhost`, `db.company.com`) - **Port**: Automatically filled (PostgreSQL: 5432, MySQL: 3306, MSSQL: 1433) or specify custom port - **Username**: Your database username - **Password**: Your database password (securely handled) - **Database Name**: The specific database to connect to **For SQLite:** - **Database Type**: Select "SQLite" from dropdown - **SQLite Download URL**: Either a local file path or HTTP URL to download the database file #### 2. Database URI For advanced users who prefer the traditional connection string format: **PostgreSQL:** ``` postgresql://username:password@hostname:port/database_name ``` **MySQL:** ``` mysql://username:password@hostname:port/database_name ``` **MSSQL:** ``` mssql://username:password@hostname:port/database_name ``` **SQLite:** ``` sqlite:///path/to/database.sqlite sqlite+https://example.com/database.sqlite ``` ## Database URI Components Protocol (postgresql://) - Database type identifier Username (admin) - Database user credentials Password (secret123) - User password (kept secure) Hostname (db.company.com) - Server address Port (5432) - Connection port Database (mydatabase) - Target database name ## Connection Process 1. **Choose your method**: Use either Connection Details form or Database URI 2. **Fill in credentials**: Provide your database connection information 3. **Click Connect**: The system will validate and test your connection 4. **Schema Generation**: Upon successful connection, the system automatically: - Connects to your database - Analyzes the database structure - Generates SQLAlchemy models - Makes schema available to the AI for queries ```md alert # NoSQL Databases NoSQL databases (e.g., MongoDB, DynamoDB) can be accessed via Python SDKs which the AI Builder can install if you prompt for it. The first class Database integration currently supports only SQL databases. ``` # Databricks Integration Source: http://localhost:3000/docs/ai/integrations/databricks.md --- tags: Data Infrastructure description: Connect with Databricks to run data pipelines and advanced analytics seamlessly. --- The **Databricks Integration** allows your app to connect to [Databricks](https://www.databricks.com/) for secure data access, querying, and analytics. Once connected, you can run SQL queries, retrieve results, and power data-driven workflows directly from your app. ## What You Can Do With Databricks, your app can: - Connect securely to your Databricks workspace. - Run **SQL queries** or fetch data programmatically. - Build **dashboards and data visualizations** on top of your Databricks tables. - Automate workflows triggered by new or updated data. - Combine Databricks with AI models for advanced analytics. ## Step 1: Get Your Databricks Credentials ### 1 - Log in to your [Databricks Workspace](https://databricks.com/). ### 2 - Get your **DATABRICKS_HOST** and **DATABRICKS_WAREHOUSE_ID**: - Go to `SQL Warehouses` from the sidebar. - Select your desired warehouse. - Click `Connection details`. - Copy the Server hostname (this is your **DATABRICKS_HOST**). - Copy the HTTP path removing the `/sql/1.0/warehouses/` prefix (this is your **DATABRICKS_WAREHOUSE_ID**). ![Databricks SQL Warehouse connection details](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_1.webp) ### 3 - Get your **DATABRICKS_CATALOG** and **DATABRICKS_SCHEMA**: - Click the SQL Editor from the sidebar. - Choose the **DATABRICKS_CATALOG** and **DATABRICKS_SCHEMA** from the dropdowns as shown below. ![Selecting Databricks catalog and schema in SQL Editor](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_4.webp) ### 4 - Obtain your authentication credentials: There are two methods to authenticate your app with Databricks: using interactive U2M **OAuth** or via registered M2M **Service Principal**. Choose one of the methods below to obtain the necessary credentials. `````md tabs ## OAuth with Databricks #### Interactive Login with your Databricks Account If your workspace administrator has already added Reflex as a Connected App in your Databricks workspace, you should see a "Login with Databricks" button after entering your DATABRICKS_HOST. ## Service Principal #### M2M Service Principal (DATABRICKS_CLIENT_ID and DATABRICKS_CLIENT_SECRET) - Open the dropdown in the top right corner and select **Manage Account**. ![Opening Manage Account in Databricks](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_1.webp) - Select `Users and Groups` ![Selecting Users and Groups in Databricks account](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_2.webp) - Select the `Service Principals` tab and click `Add Service Principal`. ![Adding a Databricks service principal](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_3.webp) - Fill in the details and click `Add Service Principal`. ![Entering Databricks service principal details](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_4.webp) - Click the `Credentials and Secrets` tab and click `Generate Secret`. ![Generating a Databricks service principal secret](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_5.webp) - Set a lifetime for the secrets and click `Generate`. ![Setting Databricks secret lifetime](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_6.webp) - Copy the generated **DATABRICKS_CLIENT_ID** and **DATABRICKS_CLIENT_SECRET**. ![Copying Databricks client ID and secret](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_integration_oauth_7.webp) ````` ## Step 2: Configure the Integration in Your App 1. In your app, go to **Integrations** and **Add Databricks**. 2. Paste your **DATABRICKS_HOST** 3. Select your authentication method: - OAuth, if configured. - Service Principal, provide the client ID and secret gathered previously. 2. Paste or select your 1. **DATABRICKS_WAREHOUSE_ID** 2. **DATABRICKS_CATALOG** 3. **DATABRICKS_SCHEMA** 3. Click **Connect** to validate and save your integration. Once connected, the AI Builder can execute queries directly against your Databricks environment. ## Step 3: Notes * **Secure your token:** Never expose tokens in public code. * **Permissions:** Ensure your token or service account has the required workspace and table permissions. * **Combine with AI:** Use query outputs to power models, summaries, or alerts in real time. ## How to Enable Login as an Admin - Login to Databricks - Go to manage account ![Opening Manage Account as Databricks admin](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_admin_auth_1.webp) - Go to **Settings** → **Apps Connections** → **Add new connection** ![Adding a new app connection in Databricks settings](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_admin_auth_2.webp) - Give it a name, enter the redirect URL that Reflex Build provides, Click `All APIs` and uncheck `Generate a client secret` and then add. - URL: `https://build.reflex.dev/_reflex_oidc_databricks/authorization-code/callback` ![Configuring the Reflex redirect URL in Databricks](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/databricks_admin_auth_3.webp) # Descope Integration Source: http://localhost:3000/docs/ai/integrations/descope.md --- tags: Authentication description: Add Descope’s identity and authentication platform to your apps to manage user sign-in, onboarding, MFA, SSO, and flows with ease. --- The **Descope Integration** enables your app to leverage Descope’s full Customer Identity & Access Management (CIAM) features—visual authentication flows, identity management, SSO, MFA, tenant isolation, and more. Once set up, you can orchestrate secure user journeys across your application with minimal coding. ```md alert warning # You must open your app in a new tab from the builder to test the Descope integration. ``` ## What You Can Do With Descope Integration, your app can: - Handle user sign-up, login, and passwordless flows seamlessly - Enable multifactor authentication (MFA), passkeys, and social login - Support single sign-on (SSO) via OIDC/SAML - Manage users, roles, tenants, and permissions via management APIs - Orchestrate onboarding, branding, and multi-tenant flows via drag-and-drop workflows - Sync user data with tools like HubSpot or Segment via built-in connectors ## Step 1: Obtain Descope Credentials ![Descope Credentials](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/descope.webp) 1. Log in to the [**Descope Console** (or sign up)](https://www.descope.com/sign-up). 2. Create your first Project. 3. Go to the Project page under Settings. 4. Copy the `Project ID`. ## Step 2: Configure the Integration in Your App 1. In your app’s settings or integration dashboard, select **Add Descope**. 2. Enter your **PROJECT_ID**. 3. (Optional) Pick a **DESCOPE_FLOW_ID** (e.g. “sign-up-or-login”) and **SESSION_SECRET_KEY** or press the Clipboard Copy to use default values. 4. Click **Connect** to validate credentials and enable Descope features. ![Descope Integration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/descope_2.webp) # Gemini Integration Source: http://localhost:3000/docs/ai/integrations/gemini.md --- tags: AI description: Connect your apps to Google’s Gemini models for advanced AI capabilities. --- The **Gemini Integration** allows your app to use [Google’s Gemini models](https://aistudio.google.com/) for text generation, reasoning, multimodal analysis, and other advanced AI capabilities. Once connected, you can call Gemini directly from your workflows, UI actions, or automated triggers. ## Step 1: Obtain a Gemini API Key 1. Go to [Google AI Studio](https://aistudio.google.com/). 2. Navigate to **Get API Key** or create a new key for your project. 3. Copy the generated key. ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Gemini** in your app settings. 2. Paste your **Gemini API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can use Gemini for AI-powered features across workflows and components. ## Step 3: Notes * **Keep your key secure:** Do not hardcode your Gemini API key in public code repositories. * **Use environment-specific keys:** Separate dev, staging, and production keys help manage access and security. * **Secure API access:** The key allows your app to interact with Gemini endpoints securely and efficiently. # GitHub Integration Source: http://localhost:3000/docs/ai/integrations/github.md --- tags: Developer Tools description: Connect your app to GitHub to automate workflows, manage repositories, and integrate developer operations. --- The **GitHub Integration** lets your app connect directly to [GitHub](https://github.com) to automate actions, fetch data, and build powerful developer workflows. Once connected, your app can interact with repositories, issues, pull requests, and more. ## What You Can Do With GitHub, your app can: - Fetch and display repository data (commits, branches, issues, etc.). - Create or update issues, pull requests, and discussions. - Trigger workflows or CI/CD pipelines. - Sync GitHub activity into your app’s dashboards or automations. - Build custom developer tools using GitHub’s API. ## Step 1: Generate a Personal Access Token 1. Go to your [GitHub Settings](https://github.com/settings/tokens). 2. Navigate to **Developer settings → Personal access tokens**. 3. Click **Generate new token** (classic or fine-grained). 4. Select the required scopes (e.g., `repo`, `workflow`, `read:user`). 5. Copy the token. **Example token:** ``` ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` (Note: Replace the above value with your actual GitHub token) > 💡 Fine-grained tokens are recommended for better security. ## Step 2: Configure the Integration in Your App 1. In your app, go to **Integrations → Add GitHub**. 2. Paste your **GitHub Personal Access Token** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can start interacting with GitHub through workflows and actions. ## Step 3: Notes * **Keep your token secure:** Never expose your GitHub token in public code. * **Use fine-grained permissions:** Limit access to only what’s needed. * **API rate limits:** GitHub imposes API limits, so plan automations accordingly. * **Combine with AI:** For example, auto-generate release notes from commits or summarize PRs with LLMs. # Google Auth Integration Source: http://localhost:3000/docs/ai/integrations/google-auth.md --- tags: Authentication description: Enable secure authentication using Google sign-in and OAuth 2.0 workflows. --- The **Google Auth Integration** allows your app to authenticate users using their Google accounts. This provides a secure, familiar login experience and simplifies user management. ## Step 1: Create a Google OAuth Client 1 - Go to the [Google Cloud Console](https://console.cloud.google.com/) 2 - Navigate to **APIs & Services → Credentials** 3 - Click **Create Credentials** ![Creating credentials in Google Cloud Console](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/google_auth_1.webp) 4 - Choose **OAuth client ID**. ![Choosing OAuth client ID credential type](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/google_auth_2.webp) 5 - Choose **Web Application** as the application type, name the application and enter your sandbox URL, which you get from the Google Auth integration panel, as the **Authorized JavaScript Origin** and **Authorized Redirect URIs**. ![Configuring Google OAuth web application origins](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/google_auth_3.webp) 6 - Copy the generated **Client ID** (`GOOGLE_CLIENT_ID`) and **Client Secret** (`GOOGLE_CLIENT_SECRET`). ![Copying Google client ID and secret](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/google_auth_4.webp) ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Google Auth** in your app settings. 2. Enter your **Google Client ID** and **Google Client Secret**. 3. Save the integration. Your app is now configured to use Google Auth for login. ## Step 3: Notes - The integration supports multiple environments (development, staging, production). Use environment-specific secrets for the client ID and secret. - Google Auth handles token refresh automatically when users log in via OAuth. - Users will see a standard Google login screen, ensuring familiarity and trust. - Ensure your app domain matches the authorized JavaScript origin and redirect URIs; otherwise, authentication will fail. # Groq Integration Source: http://localhost:3000/docs/ai/integrations/groq.md --- tags: AI description: Connect your apps to Groq’s ultra-fast inference API for lightning-speed AI responses. --- The **Groq Integration** allows your app to use [Groq’s API](https://groq.com/) for ultra-fast AI inference. Once connected, your app can leverage Groq’s hosted models (including open models like Llama 4 and Qwen) to power real-time workflows, chat experiences, and other AI-driven features. ## Step 1: Obtain a Groq API Key 1. Go to the [Groq Console](https://console.groq.com/). 2. Navigate to **API Keys** in your account settings. 3. Click **Create API Key** and give it a descriptive name (e.g., “AI Builder”). 4. Copy the generated key. * **Example:** `gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Groq** in your app settings. 2. Paste your **Groq API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can use Groq to deliver ultra-low latency AI responses across workflows and components. ## Step 3: Notes * **Keep your key secure:** Do not hardcode your Groq API key in public code repositories. * **Use environment-specific keys:** Separate dev, staging, and production keys help manage access and security. * **Secure API access:** The key allows your app to interact with Groq endpoints securely and efficiently. # HubSpot Integration Source: http://localhost:3000/docs/ai/integrations/hubspot.md --- tags: Data Infrastructure description: Connect your apps to HubSpot to manage contacts, companies, deals, and marketing workflows. --- The **HubSpot Integration** allows your app to use [HubSpot](https://www.hubspot.com/) as a CRM and marketing automation platform. Once connected, your app can sync leads, manage contacts, update pipelines, and trigger workflows directly from user actions or automated events. ## Step 1: Obtain a HubSpot Access Token 1. Go to your [HubSpot Developer Account](https://developers.hubspot.com/). 2. Navigate to **App → Private Apps** in your account settings. 3. Click **Create Private App**, configure scopes (e.g., `crm.objects.contacts.read/write`, `crm.schemas.companies.read`), and save. 4. Copy the **Access Token** from the Auth tab. * **Example:** `pat-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add HubSpot** in your app settings. 2. Paste your **HubSpot Access Token** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can sync and manage HubSpot data directly from workflows and UI actions. ## Step 3: Notes * **Keep your token secure:** Do not hardcode your HubSpot token in public code repositories. * **Use environment-specific tokens:** Separate dev, staging, and production tokens to manage access. * **Secure API access:** The token allows your app to interact with HubSpot endpoints securely and efficiently. # Hugging Face Integration Source: http://localhost:3000/docs/ai/integrations/hugging-face.md --- tags: AI description: Connect your apps to Hugging Face to access thousands of open models for text, vision, and multimodal tasks. --- The **Hugging Face Integration** allows your app to use [Hugging Face](https://huggingface.co/) models for a wide range of AI tasks — including text generation, embeddings, translation, classification, image processing, and more. You can integrate both hosted models (via API) or local open-source models through the Transformers library. ## What You Can Do With Hugging Face, your app can: - Access thousands of pre-trained **text, vision, and multimodal models**. - Run **text generation**, summarization, translation, and classification tasks. - Use **embeddings** for search, retrieval, and similarity. - Deploy models locally with `transformers` or connect to hosted inference endpoints. - Fine-tune or customize open models for your specific use cases. - Integrate community and enterprise models seamlessly into workflows. ## Step 1: Obtain a Hugging Face Access Token (Optional) 1. Go to [Hugging Face](https://huggingface.co/). 2. Log in or create a free account. 3. Navigate to **Settings → Access Tokens**. 4. Click **Create New token**, set its scope, and copy it. * **Example:** `hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Hugging Face** in your app settings. 2. Paste your **Hugging Face Access Token** if required. 3. Click **Connect** to validate and save your integration. Once connected, your app can use Hugging Face models directly in workflows, UI actions, or automations. ## Step 3: Notes * **Keep your token secure:** Never expose your Hugging Face token in client-side code. * **Use environment-specific tokens:** Separate dev, staging, and production access. * **Open-source flexibility:** Models can be run fully locally or through Hugging Face’s hosted endpoints. * **Broad ecosystem:** Hugging Face supports thousands of community and commercial models for rapid prototyping and production use. # Langchain Integration Source: http://localhost:3000/docs/ai/integrations/langchain.md --- tags: AI description: Connect your apps to Langchain for advanced AI capabilities. --- The **LangChain Integration** allows your app to leverage the power of [LangChain](https://langchain.com/), a popular open-source framework for building applications with large language models (LLMs). Once connected, your app can orchestrate complex AI workflows, chain together multiple model calls, and integrate with various data sources and tools seamlessly. You can: * **Chain model calls together** to create multi-step reasoning flows. * **Connect multiple providers** (e.g., OpenAI, Anthropic, Gemini, Groq) through a single interface. * **Build intelligent agents** that can use tools, call APIs, or access external data sources dynamically. * **Work with structured data** by combining LLMs with databases, vector stores, and retrieval systems. * **Create RAG (Retrieval-Augmented Generation)** pipelines to ground model outputs in your own data. * **Integrate tools and memory** so models can reason over past interactions and context. * **Deploy reusable components** like prompt templates, chains, and agents for production-grade applications. LangChain essentially acts as a **“glue layer”** between language models, data, and logic — enabling teams to build more **powerful and reliable AI apps** without reinventing core orchestration features. ## Configure the Integration in Your App 1. Click the **Settings** gear icon and navigate to the **Integrations** tab. 2. Find and enable the **LangChain** integration. # Linear Integration Source: http://localhost:3000/docs/ai/integrations/linear.md --- tags: DevTools description: Connect your apps to Linear to manage issues, projects, and sprints directly from workflows. --- The **Linear Integration** allows your app to connect to [Linear](https://linear.app/) to create, update, and track issues and projects seamlessly. Once connected, your app can trigger Linear actions from user interactions or automated workflows. ## What You Can Do With Linear, your app can: - **Create and assign issues** automatically from user actions or AI workflows. - **Update status and priority** of tickets and projects. - **Sync with sprints and cycles** to keep work organized. - **Fetch project and issue data** for dashboards, summaries, or automation. - **Integrate with other tools** like Slack or GitHub through workflows. - Power intelligent workflows (e.g., auto-triage or auto-summarize bug reports). ## Step 1: Obtain a Linear API Key 1. Go to your [Linear account](https://linear.app/). 2. Navigate to **Workspace Settings → API**. 3. Click **Create API Key**, give it a name, and copy the key. * **Example:** `lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Linear** in your app settings. 2. Paste your **Linear API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can interact with Linear to automate issue tracking and project management. ## Step 3: Notes * **Keep your key secure:** Never expose your Linear API key in client-side code. * **Use environment-specific keys:** Separate dev, staging, and production keys. * **Streamlined workflows:** Automate ticket creation, status updates, and reporting directly from your app. * **Real-time visibility:** Fetch and sync project data to power internal tools or dashboards. # Installation Source: http://localhost:3000/docs/ai/integrations/mcp-installation.md ```python exec import reflex as rx ``` ```md alert warning # The Reflex MCP integration is currently only available for enterprise customers. Please [book a demo](https://reflex.dev/pricing/) to discuss access. ``` To use the Reflex MCP integration, you'll need to configure your AI assistant or coding tool to connect to the Reflex MCP server. No additional Python packages are required on your local machine - the server is hosted and ready to use. ## Prerequisites - An MCP-compatible AI tool (Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, Windsurf, etc.) - Internet connection to access the hosted MCP server - Valid Reflex account for OAuth 2.1 authentication ## Authentication The Reflex MCP server uses OAuth 2.1 protocol for secure authentication. You'll need a valid Reflex account, and authentication is handled automatically through your MCP client configuration when you provide your Reflex credentials. ## IDE and Coding Assistant Integration ### Claude Code Add the Reflex MCP server to Claude Code by running: ```bash claude mcp add --transport http reflex https://build.reflex.dev/mcp ``` Then authenticate by running the `/mcp` command inside Claude Code and following the login steps in your browser. Authentication tokens are stored securely and refreshed automatically. See the [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp) for more details. ### Claude Desktop Claude Desktop pulls remote MCP servers from your Claude account's connectors. Go to [claude.ai](https://claude.ai) → **Settings** → **Connectors** → **Add custom connector**, enter `https://build.reflex.dev/mcp` as the URL, and complete the OAuth login. The connector will then be available in Claude Desktop after you sign in. See the [custom connectors guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) for details and plan availability. ### Codex Add the Reflex MCP server to Codex by running: ```bash codex mcp add reflex --url https://build.reflex.dev/mcp ``` See the [Codex MCP documentation](https://developers.openai.com/codex/mcp) for more details. ### Cursor [Click here to install the Reflex MCP server in Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=reflex&config=eyJ1cmwiOiJodHRwczovL2J1aWxkLnJlZmxleC5kZXYvbWNwIn0=), or edit (or create) `~/.cursor/mcp.json` for a global config, or `.cursor/mcp.json` in your project root for a project-specific config: ```json { "mcpServers": { "reflex": { "url": "https://build.reflex.dev/mcp" } } } ``` Open Cursor settings under **MCP & Integrations** to verify the server is connected and complete OAuth login. See the [Cursor MCP documentation](https://cursor.com/docs/context/mcp) for more details. ### Gemini CLI Add the Reflex MCP server to `~/.gemini/settings.json`: ```json { "mcpServers": { "reflex": { "httpUrl": "https://build.reflex.dev/mcp" } } } ``` See the [Gemini CLI MCP documentation](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html) for more details. ### GitHub Copilot Create a `.vscode/mcp.json` file in your project root (or open **MCP: Open User Configuration** from the VS Code command palette for a global config): ```json { "servers": { "reflex": { "type": "http", "url": "https://build.reflex.dev/mcp" } } } ``` After saving, start the server from the inline action above the entry in `mcp.json`, then complete the OAuth login when prompted. See the [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers) for more details. ### OpenCode Add the Reflex MCP server to your `opencode.json` (project) or `~/.config/opencode/opencode.json` (global): ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "reflex": { "type": "remote", "url": "https://build.reflex.dev/mcp", "enabled": true } } } ``` See the [OpenCode MCP documentation](https://opencode.ai/docs/mcp-servers/) for more details. ### Windsurf Edit (or create) `~/.codeium/windsurf/mcp_config.json` and add the Reflex server: ```json { "mcpServers": { "reflex": { "serverUrl": "https://build.reflex.dev/mcp" } } } ``` After saving, open Cascade and click the refresh icon in the MCP toolbar to load the new server. See the [Windsurf MCP documentation](https://docs.windsurf.com/windsurf/cascade/mcp) for more details. # Overview Source: http://localhost:3000/docs/ai/integrations/mcp-overview.md ```python exec import reflex as rx ``` ```md alert warning # The Reflex MCP integration is currently only available for enterprise customers. Please [book a demo](https://reflex.dev/pricing/) to discuss access. ``` The Reflex [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) integration provides AI assistants and coding tools with structured access to Reflex framework documentation and component information. This enables intelligent assistance while developing Reflex applications. The Reflex MCP server is deployed at `https://build.reflex.dev/mcp` and provides access to component documentation and Reflex documentation through standardized MCP tools. ## Available Tools The Reflex MCP server provides tools for accessing component documentation and Reflex documentation through standardized MCP capabilities. ## Enterprise Use For enterprise customers requiring on-premises deployment of the Reflex MCP server, please [book a demo](https://reflex.dev/pricing/) to discuss your requirements. # Notion Integration Source: http://localhost:3000/docs/ai/integrations/notion.md --- tags: Data Infrastructure description: Connect your apps to Notion to create, update, and query pages, databases, and documents. --- The **Notion Integration** allows your app to connect to [Notion](https://www.notion.so/) to manage content, automate workflows, and build dynamic interfaces on top of Notion workspaces. Once connected, your app can read and write pages, sync databases, and trigger actions based on user activity. ## What You Can Do With Notion, your app can: - **Create and update pages** in workspaces automatically. - **Read and query databases** to power dashboards, workflows, or automations. - **Sync structured content** like tasks, notes, or documentation with other tools. - Trigger **actions from AI workflows** (e.g., creating meeting notes, logging issues). - Integrate with other services (e.g., Slack, Linear, HubSpot) for seamless collaboration. ## Step 1: Obtain a Notion API Token 1. Go to the [Notion Developers](https://www.notion.so/my-integrations) page. 2. Click **+ New Integration**, give it a name, and copy the generated **Internal Integration Token**. 3. Share the integration with the relevant pages or databases in your workspace to grant access. * **Example:** `ntn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Notion** in your app settings. 2. Paste your **Notion API Token** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can read and write to Notion directly from workflows and UI actions. ## Step 3: Notes * **Keep your token secure:** Never expose your Notion token in client-side code. * **Use environment-specific tokens:** Separate dev, staging, and production access. * **Granular permissions:** Only share the integration with the pages or databases it needs to access. * **Powerful building block:** Use Notion as a single source of truth for content, tasks, or structured data. # Okta Auth Manager Integration Source: http://localhost:3000/docs/ai/integrations/okta-auth.md --- tags: Authentication description: Use Okta for secure identity and access management via Single Sign-On provisioning. --- The **Okta Auth Manager Integration** allows your app to authenticate users through [Okta](https://okta.com). This integration provides secure OAuth 2.0 / OIDC authentication and supports multi-tenant environments with customizable access policies. ## What You Can Do With Okta, your app can: - Authenticate users securely through Okta’s identity platform. - Enable **SSO** for enterprise users. - Manage user roles, groups, and access permissions. - Protect sensitive data and actions with **OAuth 2.0** and **OpenID Connect (OIDC)**. - Integrate with other identity workflows like MFA or adaptive policies. ## Step 1: Set Up Okta OIDC App Before connecting, you need to create an OIDC application in the Okta Admin Console: 1 - Go to [Okta Admin Console](https://login.okta.com) → **Applications** → **Applications** 2 - Click **Create App Integration** ![Creating an Okta app integration](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/okta_auth_1.png) 3 - Select **OIDC – OpenID Connect** and choose **Web Application** ![Selecting OIDC web application in Okta](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/okta_auth_2.png) 4 - Configure your app settings: - **Allow wildcard * in sign-in redirect URIs** - **Sign-in redirect URIs** found in the Okta Auth Manager integration settings in AI Builder: `https://{your-sandbox}/authorization-code/callback` - **Sign-out redirect URIs**: `https://{your-sandbox}` - Assign to the correct **Group** or **Everyone** depending on your access control ![Configuring Okta sign-in redirect URIs](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/okta_auth_3.png) 5 - Save the app integration. 6 - Copy your **Client ID** (`OKTA_CLIENT_ID`) and **Client Secret** (`OKTA_CLIENT_SECRET`) from the app settings. ![Copying Okta client ID and secret](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/okta_auth_4.png) ## Step 2: Finding Your Okta Issuer URI 1. In the Okta Admin Console, go to **Security** → **API** → **Authorization Servers** 2. Click on the **default** server and copy the **Issuer URI**. 3. Remove the trailing `/oauth2/default` from the URI to get your **Okta Issuer URI** (`OKTA_ISSUER_URI`). Example: If your Issuer URI is `https://{yourOktaDomain}.okta.com/oauth2/default` Use `https://{yourOktaDomain}.okta.com` ![Finding the Okta issuer URI](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/okta_auth_5.png) > **Note:** Always use separate Okta apps for dev, staging, and production environments to avoid mixing credentials. ## Step 3: Configure the Integration 1. Go to the **Integrations** section in your app settings by clicking **`@`** and then selecting the **Integrations** tab. 2. Click **Add** next to **Okta Auth Manager**. 3. Fill in the credential fields: - Enter your Okta Client ID - Enter your Okta Client Secret - Enter your Okta Issuer URI 4. Click **Connect** to save the integration. Your app can now authenticate users through Okta using the secure OAuth 2.0 / OIDC flow. # OpenAI Integration Source: http://localhost:3000/docs/ai/integrations/openai.md --- tags: AI description: Connect to OpenAI's powerful language models for text generation, analysis, and more. --- The **OpenAI Integration** allows your app to use OpenAI APIs for features such as text generation, embeddings, and other AI-powered functionality. ## Step 1: Obtain an OpenAI API Key 1. Go to the [OpenAI Platform](https://platform.openai.com/). 2. Navigate to **API Keys** in your account settings. 3. Click **Create new secret key**. 4. Copy the generated key. - Example: `sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add OpenAI Integration** in your app settings. 2. Enter your **OpenAI API Key** in the input field. 3. Save the integration. Your app is now ready to make OpenAI API requests. --- ## Step 3: Notes - Keep your OpenAI key secure; do **not** hardcode it in public code repositories. - Use environment-specific secrets if you have separate development, staging, and production environments. - The key allows your app to interact with OpenAI endpoints securely and efficiently. # Overview Source: http://localhost:3000/docs/ai/integrations/overview.md ```python exec from reflex_docs.pages.integrations.integration import integration_page ``` ```python eval integration_page() ``` # Perplexity Integration Source: http://localhost:3000/docs/ai/integrations/perplexity.md --- tags: AI description: Connect your apps to Perplexity to power real-time, retrieval-augmented AI experiences. --- The **Perplexity Integration** allows your app to use [Perplexity](https://www.perplexity.ai/) to deliver fast, accurate, retrieval-augmented answers from the web and trusted sources. Once connected, you can use Perplexity to enhance search, summarization, and real-time knowledge in your workflows. ## What You Can Do With Perplexity, your app can: - **Retrieve real-time information** from the web to ground model outputs. - **Generate concise summaries** and answers with citations. - Power **search experiences**, research assistants, and contextual AI features. - **Combine Perplexity with other models** for hybrid workflows (e.g., retrieval + reasoning). - Enable **trusted, source-backed outputs** for more accurate responses. ## Step 1: Obtain a Perplexity API Key 1. Go to the [Perplexity](https://www.perplexity.ai/). 2. Log in or create an account. 3. Navigate to **Account → API → [API Keys](https://www.perplexity.ai/account/api/keys)**. 4. Click **Generate Key** and copy the generated key. * **Example:** `pplx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Perplexity** in your app settings. 2. Paste your **Perplexity API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can use Perplexity for retrieval-augmented AI features across workflows and components. ## Step 3: Notes * **Keep your key secure:** Do not hardcode your Perplexity API key in public code repositories. * **Use environment-specific keys:** Separate dev, staging, and production keys for better control. * **Secure API access:** The key allows your app to interact with Perplexity endpoints securely and efficiently. * **Real-time power:** Perplexity is ideal for use cases where freshness and factual grounding matter. # Replicate Integration Source: http://localhost:3000/docs/ai/integrations/replicate.md --- tags: AI description: Connect your apps to Replicate to run and deploy open-source machine learning models with ease. --- The **Replicate Integration** allows your app to use [Replicate](https://replicate.com/) to run open-source AI models in the cloud without managing infrastructure. Once connected, you can use Replicate to generate text, images, audio, and more — directly from workflows and UI actions. ## What You Can Do With Replicate, your app can: - Run open-source **AI models** without GPU or hosting setup. - Access models for **text generation**, **image generation**, **audio**, and **multimodal tasks**. - Automate workflows like content generation, data processing, or AI-powered features. - Use prebuilt models or deploy your own custom models. - Scale effortlessly with serverless infrastructure. ## Step 1: Obtain a Replicate API Token 1. Go to the [Replicate Dashboard](https://replicate.com/account). 2. Log in or create an account. 3. Navigate to the **API Tokens** section. 4. Copy your **API Token** from the account page. * **Example:** `r8_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Replicate** in your app settings. 2. Paste your **Replicate API Token** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can call Replicate models directly in workflows, UI actions, or automations. ## Step 3: Notes * **Keep your token secure:** Do not hardcode your Replicate token in public code repositories. * **Use environment-specific tokens:** Separate dev, staging, and production environments for security. * **Secure API access:** The token allows your app to interact with Replicate endpoints safely and efficiently. * **Model flexibility:** You can use community models or deploy your own for full control. # Resend Integration Source: http://localhost:3000/docs/ai/integrations/resend.md --- tags: Communication description: Send transactional and automated emails directly from your AI-generated applications using Resend. --- The **Resend Integration** allows your AI-generated apps to send emails directly from workflows, user actions, or automated triggers — perfect for notifications, confirmations, or any email-based communication your app needs. ## What You Can Do With the Resend Integration, your app can: - Send automated transactional emails - Notify users of important events (signups, job completions, etc.) - Integrate email sending into AI workflows - Use dynamic variables in your message body (e.g. user name, order ID) ## Step 1: Get Your Resend API Key 1. Go to [Resend](https://resend.com/). 2. Log in or create a free account. 3. Navigate to **API Keys** from the dashboard sidebar. 4. Click **Create API Key**, give it a name (e.g. “AI Builder”), and copy it. - Example: `re_xxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Open your app in the **AI Builder**. 2. Click the **Settings** gear icon and navigate to the **Integrations** tab. 3. Find and enable the **Resend** integration. 4. Paste your **API Key** in the input field. 5. Click **Connect** to confirm the connection. # Roboflow Integration Source: http://localhost:3000/docs/ai/integrations/roboflow.md --- tags: AI description: Connect your apps to Roboflow to use computer vision models and datasets for image detection, classification, and segmentation. --- The **Roboflow Integration** allows your app to use [Roboflow](https://roboflow.com/) to run and deploy computer vision models without complex infrastructure. Once connected, you can use Roboflow to power image detection, object tracking, classification, and segmentation workflows. ## What You Can Do With Roboflow, your app can: - Run **pre-trained computer vision models** for detection, classification, or segmentation. - Deploy and use **custom-trained models** built in Roboflow. - Automate workflows that rely on visual inputs — e.g., quality checks, object detection, image analysis. - Ingest and process **images in real time**. - Combine vision outputs with other integrations (e.g., AI reasoning, automation flows). ```md alert ## This integration currently supports image inputs only. Video and streaming inputs are not yet supported. ``` ## Step 1: Obtain a Roboflow API Key 1. Go to your [Roboflow Account](https://roboflow.com/). 2. Navigate to **Settings → API Keys**. 3. Copy your Private API Key. ## Step 2: Set up your Model in Roboflow 1. Go to Workflows and create a new workflow. 2. Set up the workflow you want to use in your app, ensuring that it takes an image as input. 3. Click the `deploy` button, select `Images` and then select `Integrate with my app or website`. 4. Copy the `workspace_name` and `workflow_id` from the provided code snippet. ![Roboflow](https://raw.githubusercontent.com/reflex-dev/integrations-docs/refs/heads/main/images/docs/roboflow.webp) ## Step 3: Configure the Integration in Your App 1. Go to **Integrations → Add Roboflow** in your app settings. 2. Paste your **Roboflow API Key**, **Workspace Name**, and **Workflow ID** in the input fields. 3. Click **Connect** to validate and save your integration. Once connected, your app can call Roboflow models directly from workflows, UI actions, or automated triggers. ## Step 4: Notes * **Keep your key secure:** Never expose your Roboflow key in client-side code. * **Use environment-specific keys:** Separate dev, staging, and production keys to control access. * **Model flexibility:** Use community models or your own custom-trained models. * **Real-time vision:** Ideal for applications involving cameras, inspection, monitoring, or visual AI. # Skills Source: http://localhost:3000/docs/ai/integrations/skills.md ```python exec import reflex as rx def _summary_card(kicker: str, title: str, body: str) -> rx.Component: return rx.el.div( rx.el.div(kicker, class_name="text-xs font-semibold uppercase text-primary-10"), rx.el.h3(title, class_name="text-base font-semibold text-secondary-12"), rx.el.p(body, class_name="text-sm leading-6 text-secondary-11"), class_name="flex flex-col gap-2 rounded-lg border border-secondary-a4 bg-white-1 p-4", ) def skills_summary_cards() -> rx.Component: return rx.el.div( _summary_card( "Docs", "Current Reflex guidance", "Point agents to the right Reflex docs for state, vars, components, routing, styling, deployment, and more.", ), _summary_card( "Setup", "Python environment workflow", "Teach agents how to create a virtual environment, install Reflex, and initialize a new project safely.", ), _summary_card( "Runtime", "Process management", "Give agents a repeatable way to compile, run, restart, and debug Reflex apps without guessing at processes.", ), _summary_card( "Context", "Pairs well with MCP", "Use skills for durable local instructions and MCP for structured lookup of current docs and component data.", ), class_name="grid grid-cols-1 gap-3 md:grid-cols-2 my-6", ) ``` Reflex Agent Skills give AI coding assistants up-to-date guidance for building Reflex applications. They package Reflex-specific knowledge, setup steps, and process-management workflows into reusable `SKILL.md` files that agents can load when the conversation or codebase calls for them. The skills are maintained in the [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository and are designed for agents that support the Agent Skills standard, including Claude Code, Cursor, OpenCode, OpenAI Codex, and Pi. ```python eval skills_summary_cards() ``` ## When to Use Skills Use Reflex Agent Skills when you want an AI assistant to follow Reflex-specific workflows instead of relying only on general training data. They are especially useful when an assistant needs to: - Build or edit a Reflex app. - Set up a new Python environment. - Decide which Reflex docs apply to the task. - Compile, run, restart, or debug a local Reflex server. - Keep generated code aligned with current Reflex patterns. Skills load contextually. For example, when an assistant sees a Python file importing `reflex as rx`, it can load the Reflex docs skill. When you ask it to start a new app, it can load the Python environment setup skill before running project commands. ## Skills vs MCP Skills and MCP solve related but different problems. For the best experience, use both. ```md definition # Skills Local instruction packs that tell the agent how to work with Reflex, which workflows to follow, and which references matter for common development tasks. # MCP Structured runtime access to Reflex documentation and component information through a hosted server. Use it when an agent or editor can call MCP tools directly. ``` ```md alert info # Use Skills for durable local guidance. Use MCP for structured documentation lookup and richer tool-assisted context. ``` ## Installation Choose the install path that matches your assistant. `````md tabs ## Claude Code Install the plugin from the Claude Code plugin marketplace: ```text /plugin marketplace add reflex-dev/agent-skills /plugin install reflex@reflex-agent-skills ``` Restart or refresh Claude Code after installation if the skills do not appear immediately. ## Cursor Install from the Cursor Marketplace, or add the repository manually from: ```text reflex-dev/agent-skills ``` In Cursor, add it through **Settings > Rules > Add Rule > Remote Rule (GitHub)**. ## CLI If your environment supports the `skills` CLI, install the package with: ```bash npx skills add reflex-dev/agent-skills ``` Use the CLI's update command later to keep the skill pack current. ## Manual Clone the repository: ```bash git clone https://github.com/reflex-dev/agent-skills.git ``` Then copy the folders inside `skills/` into the appropriate location: | Agent | Skill Directory | | --- | --- | | Claude Code | `~/.claude/skills/` | | Cursor | `~/.cursor/skills/` | | OpenCode | `~/.config/opencode/skills/` | | OpenAI Codex | `~/.codex/skills/` | | Pi | `~/.pi/agent/skills/` | Copy the skill folders themselves, not the parent `skills/` directory. ````` ## Included Skills The Reflex skill pack includes three core skills. `````md tabs ## Docs The `reflex-docs` skill gives the assistant a Reflex-specific reference map and a summary of the framework's core concepts. ### Use this skill when the assistant is: - Building full-stack Python web apps with Reflex. - Writing files that import `reflex` or use the `rx` namespace. - Creating components. - Managing state, vars, computed vars, or event handlers. - Working with routing, styling, database models, assets, authentication, client storage, API routes, custom components, or wrapped React components. ### It points the assistant to docs for: - Core app structure: getting started, components, state and vars, events, pages, and routing. - UI and styling: styling, assets, the component library, and recipes. - Backend features: database models, authentication, client storage, API routes, and API reference. - Advanced integrations: custom components and wrapped React components. It also reminds the assistant to prefer current Reflex documentation over pre-trained knowledge when there is a conflict. ## Setup The `setup-python-env` skill guides the assistant through setting up a Python environment for a Reflex app. ### Use this skill when: - Starting a new Reflex project. - Setting up a development environment. - There is no `.venv` directory. - Reflex imports fail because dependencies are missing. ### Preferred workflow: ```bash uv venv .venv source .venv/bin/activate uv add reflex reflex init ``` If `uv` is not available, the skill falls back to: ```bash python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install reflex reflex init ``` The workflow checks for an existing `.venv`, verifies Python 3.10 or newer, and installs Reflex only when needed. ## Process The `reflex-process-management` skill teaches the assistant how to compile, run, reload, and debug Reflex apps. ### Use this skill when: - Testing that a Reflex app compiles. - Starting a local Reflex server. - Restarting a server after code changes. - Reading logs to diagnose app errors. ### Validation command: ```bash reflex compile --dry ``` ### Run command: ```bash reflex run --env prod --single-port 2>&1 | tee reflex.log ``` Production mode does not hot reload. To apply code changes, the assistant should stop the listening process for the app port and restart the server: ```bash lsof -i : -sTCP:LISTEN -t kill -INT $(lsof -i : -sTCP:LISTEN -t) ``` Using `-sTCP:LISTEN` helps the assistant target the server process instead of browser connections. ````` ## Recommended Workflow `````md tabs ## New App 1. Install Reflex Agent Skills in your assistant. 2. Ask the assistant to create or initialize a Reflex app. 3. The assistant should load `setup-python-env`. 4. After the environment is ready, the assistant can run `reflex init`. 5. As the app is built, the assistant should use `reflex-docs` for current framework guidance. 6. Before handing the app back, the assistant should use `reflex-process-management` to compile or run the project. ## Existing 1. Open your Reflex project in an agent-enabled editor. 2. Ask for the feature, bug fix, or refactor you want. 3. The assistant should load `reflex-docs` when it sees Reflex code. 4. For local verification, the assistant should compile the app with `reflex compile --dry` or run it with the process-management workflow. ## Debugging 1. Ask the assistant to inspect the error. 2. The assistant should read `reflex.log` if the app was started through the process-management workflow. 3. The assistant should identify the failing import, component, event handler, route, or state update. 4. After applying a fix, the assistant should restart the app if it is running in production mode. ````` ## Keeping Skills Updated Because Reflex evolves quickly, update the skill pack regularly. If you installed through a marketplace or skills CLI, update through that same tool. If you cloned the repository manually, pull the latest changes: ```bash cd agent-skills git pull ``` Then copy the updated skill folders back into your assistant's skills directory if your setup does not read directly from the cloned repository. ## Troubleshooting `````md tabs ## Loading Check that: - Your assistant supports Agent Skills. - The skill folders are in the correct skills directory. - Each skill folder contains a `SKILL.md` file. - The assistant was restarted or refreshed after installation if required. ## Outdated Advice Ask the assistant to use the Reflex docs skill, or pair the skill pack with the Reflex MCP integration for structured access to current docs and component information. ## Commands Ask the assistant to follow the `setup-python-env` skill again and verify: - The virtual environment is active. - Python is version 3.10 or newer. - Reflex is installed in the active environment. - The command is being run from the project root. ## App Updates If the app was started with `--env prod`, it will not hot reload. Restart the server with the `reflex-process-management` workflow. ````` ## Contributing Each skill lives in `skills//` and contains a `SKILL.md` manifest. To contribute new guidance, update or add a skill in the [reflex-dev/agent-skills](https://github.com/reflex-dev/agent-skills) repository and follow the [Agent Skills spec](https://agentskills.io/) for the skill format. # Stripe Integration Source: http://localhost:3000/docs/ai/integrations/stripe.md --- description: Connect your apps to Stripe to accept payments, manage subscriptions, and handle billing securely. --- The **Stripe Integration** allows your app to use [Stripe](https://stripe.com/) to power secure payments, subscriptions, and billing workflows. Once connected, you can process transactions, manage customers, and trigger payment flows directly from your app. ## What You Can Do With Stripe, your app can: - **Accept one-time payments** or set up **recurring subscriptions**. - **Manage customers**, payment methods, and invoices. - Handle **refunds, payment confirmations**, and notifications. - Automate billing flows and webhook-based actions. - Build secure, PCI-compliant checkout experiences with ease. ## Step 1: Obtain a Stripe API Key 1. Go to your [Stripe Dashboard](https://dashboard.stripe.com/). 2. Navigate to **Developers → API Keys**. 3. Copy your **Secret Key** (or create a restricted key for added security). * **Example:** `sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` > 💡 Use test keys in development environments and live keys in production. ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Stripe** in your app settings. 2. Paste your **Stripe API Key** in the input field. 3. Click **Connect** to validate and save your integration. Once connected, your app can process payments and manage billing directly from workflows and UI actions. ## Step 3: Notes * **Keep your API key secure:** Never expose your Stripe key in client-side code. * **Use environment-specific keys:** Test keys for staging, live keys for production. * **Secure transactions:** All payment processing is handled through Stripe’s PCI-compliant infrastructure. # Supabase Integration Source: http://localhost:3000/docs/ai/integrations/supabase.md --- tags: Data Infrastructure description: Connect your apps to Supabase to use a hosted Postgres database with real-time capabilities and powerful APIs. --- The **Supabase Integration** allows your app to connect to [Supabase](https://supabase.com/) — a hosted Postgres database with real-time subscriptions, authentication, and file storage. Once connected, your app can query and update data securely, power dashboards, and sync workflows in real time. ## What You Can Do With Supabase, your app can: - **Read and write data** using Postgres through a simple REST or client API. - Enable **real-time updates** that sync automatically to your UI. - Use **row-level security** and access control for safe data management. - Store and retrieve files with Supabase Storage. - Integrate seamlessly with AI, dashboards, or internal tools. ## Step 1: Obtain Your Supabase URL and Key 1. Go to your [Supabase Project](https://supabase.com/). 2. Choose the project you want to connect. 3. Navigate to **Project Settings**. 4. Go to **Data API** and copy your `Supabase_URL`. 5. Then go to **API Keys** and copy your `Supabase_Key` (the secret key). * **Example URL:** `https://your-project.supabase.co` * **Example Key:** `sb_xxxxxxxxxxxxxxxxxxxxxxxxxxxx.` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Supabase** in your app settings. 2. Paste your **Supabase URL** and **Supabase Key** into the fields. 3. Click **Connect** to validate and save your integration. Once connected, your app can query and update your Supabase database directly from workflows and UI actions. ## Step 3: Notes * **Keep your keys secure:** Never expose the `Supabase_Key` key in client-side code. * **Use environment-specific keys:** Separate dev, staging, and production projects for clean access control. * **Realtime support:** Supabase enables live syncing of data changes. * **Row-level security:** Make sure to configure policies appropriately for your use case. # Twilio Integration Source: http://localhost:3000/docs/ai/integrations/twilio.md --- tags: Communication description: Connect your apps to Twilio to send SMS, WhatsApp messages, and voice calls programmatically. --- The **Twilio Integration** allows your app to use [Twilio](https://www.twilio.com/) to send and receive SMS, WhatsApp messages, and voice calls. Once connected, your app can trigger messages or calls directly from workflows, user actions, or automated events. ## What You Can Do With Twilio, your app can: - **Send SMS** messages to users or customers globally. - **Send and receive WhatsApp messages**. - **Make and manage voice calls** programmatically. - Automate **notifications**, **alerts**, and **transactional messages**. - Integrate messaging into AI workflows for dynamic, real-time communication. ## Step 1: Obtain Twilio Credentials 1. Go to your [Twilio Console](https://www.twilio.com/console). 2. Copy your **Account SID** and **Auth Token** from the dashboard. * **Example Account SID:** `ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` * **Example Auth Token:** `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` ## Step 2: Configure the Integration in Your App 1. Go to **Integrations → Add Twilio** in your app settings. 2. Paste your **Account SID** and **Auth Token** in the input fields. 3. Click **Connect** to validate and save your integration. Once connected, your app can send messages or make calls through Twilio directly in workflows and components. ## Step 3: Notes * **Keep your credentials secure:** Never expose your Auth Token in client-side code. * **Use environment-specific credentials:** Separate dev, staging, and production credentials. * **Phone number setup:** Ensure you’ve configured a valid Twilio number for sending messages or calls. * **Regulatory compliance:** Review Twilio’s messaging and voice compliance requirements for your region. # Audit Logs Source: http://localhost:3000/docs/ai/organization/audit-logs.md --- tags: Organization description: Review a record of who did what in your Reflex organization and projects, with search, filtering, and CSV export. --- ```python exec import reflex as rx ``` An **audit log** records the important actions taken in your organization: who did what, and when. It answers questions like *who removed this member?* or *when was this domain verified?* for security reviews, compliance, or day-to-day troubleshooting. Reflex keeps audit logs at two levels: one for the **organization** and one for each **project**. ## The organization audit log Open your organization's **Settings → Audit log** to see organization-wide activity, such as member and role changes, domain and single sign-on changes, and other administrative actions. Each entry shows: - **Time**: when the action happened. - **Event**: what happened, with a short summary. - **Actor**: who did it. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/audit-logs/org_audit_log.webp", alt="The organization audit log table showing Time, Event, and Actor columns with a search box and event filter", class_name="rounded-md h-auto", ) ``` ```md alert info # Who can view the audit log The organization audit log is available to organization **admins** and **managers**. Members don't have access. ``` ### Finding an entry The audit log supports: - **Search** across events, actors, and details. - **Filter** by event type. - **Export to CSV** for reporting or to share with your security team. - **Refresh** for the latest activity. ## Project audit logs Each project has its own audit log, under the project's **Settings → Audit log**, covering activity within that project. Viewing it requires the **View audit log** permission, which comes with the project **Admin** role and can be added to a [custom role](/docs/ai/organization/custom-roles/). ## Related - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — who can see each audit log. - [Deployment approvals](/docs/ai/organization/deployment-approvals/) — a checkpoint before deployments run. # Bring Your Own Cloud Source: http://localhost:3000/docs/ai/organization/cloud-providers.md --- tags: Organization description: Connect your own cloud provider account to a Reflex organization so its apps deploy to infrastructure you control. --- ```python exec import reflex as rx ``` By default, apps run on Reflex's infrastructure. On the **Enterprise** plan, you can connect your organization's own cloud account instead, so apps run on infrastructure you control and are billed directly by your provider. This is often required by security, procurement, or data-residency policies. You connect a cloud account once, at the organization level, under **Settings → Cloud providers**. The whole organization can then deploy to it. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/cloud-providers/providers_overview.webp", alt="The Cloud providers tab showing Google Cloud connected, with AWS and Azure greyed out and unavailable", class_name="rounded-md h-auto", ) ``` ```md alert info # Who can connect a cloud provider Only organization admins can connect, view, or remove cloud provider accounts. Connecting is an Enterprise feature; [contact sales](https://reflex.dev/pricing/) to enable it. ``` ## Supported providers **Google Cloud** is available now. **AWS** and **Azure** aren't available yet. ## Connecting Google Cloud Connecting Google Cloud takes two steps: 1. **Run the setup.** Reflex provides a script that prepares your Google Cloud project and creates the credentials it needs. Select **Copy agent setup** to copy a prompt you can hand to an AI coding agent to run the steps for you. 2. **Enter the details.** Select **Connect GCP** and paste the values the setup produced: - the **service account key** (the contents of the key file), - the **project number**, - the **Cloud Run region** where apps should run, and - optionally, an **Artifact Registry repository** name. Reflex validates the details and marks the provider connected. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/cloud-providers/connect_gcp.webp", alt="The Connect your GCP account dialog with fields for the service account key, project number, and region", class_name="rounded-md h-auto", ) ``` ```md alert info # Your credentials are kept safe The service account key is stored encrypted and used only to deploy and manage your organization's apps. ``` Once connected, the tab shows the account's status, project, region, and repository. ## Removing a connection To disconnect, open the connected provider and select **Remove connection**. Two effects: - New deployments to that cloud fail until an admin reconnects an account. - Apps already running there keep running, but Reflex can't manage them. ## Deploying to your own cloud Connecting an account here lets your organization's apps target your cloud. You can also deploy to your own cloud from the command line. For the full workflow and provider details, see: - [Bring Your Own Cloud](/docs/hosting/bring-your-own-cloud/) — deploying to AWS, GCP, or Azure from the command line. - [Deploy to GCP Cloud Run](/docs/hosting/deploy-to-gcp/) — a detailed Google Cloud walkthrough. ## Workload isolation and namespaces Each organization's apps run in their own isolated space on the underlying infrastructure, separate from other organizations. With bring-your-own-cloud, that isolation is inside your account. This separation is called a *namespace*. Reflex sets it up and manages it; you don't configure it yourself. Enterprise customers with specific isolation requirements can discuss custom arrangements with the Reflex team. ## Related - [Bring Your Own Cloud (CLI)](/docs/hosting/bring-your-own-cloud/) — deploy from the command line. - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — who can manage organization settings. # Custom Roles Source: http://localhost:3000/docs/ai/organization/custom-roles.md --- tags: Organization description: Create custom project roles in Reflex, built on a base access level with the specific permissions your team needs. --- ```python exec import reflex as rx ``` The built-in project roles (Viewer, Editor, and Admin) cover most cases, but sometimes you need something in between: a contractor who can build and deploy apps but nothing else, or a reviewer who can approve deployments without editing. A **custom role** defines that combination. Custom roles are created per project, under **Settings → Roles**, by anyone with the project **Admin** role. ```md alert info # Custom roles are for teams Custom roles matter once you have teammates on a project. Inviting teammates is an Enterprise feature; see [Managing project access](/docs/ai/organization/project-access/). ``` ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/custom-roles/roles_list.webp", alt="The project Roles tab listing built-in roles and a custom role, with a New role button", class_name="rounded-md h-auto", ) ``` ## How a custom role is built A custom role has two parts: 1. **Base access level**: Viewer, Editor, or Admin. The role includes everything that level can do. 2. **Additional permissions**: individual abilities you add on top of the base level. Pick the closest built-in level, then add the permissions you need. ## Creating a role Select **New role** and fill in: - **Role name**: a label your team will recognize, such as *Support lead* or *Release manager*. - **Base access**: the starting level (Viewer, Editor, or Admin). - **Additional permissions**: the abilities to add on top. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/custom-roles/create_role_dialog.webp", alt="The Create role dialog showing a name field, a base access dropdown, and grouped permission checkboxes", class_name="rounded-md h-auto", ) ``` Permissions that come with the base level are shown ticked and greyed out; they're part of the role and can't be removed. You choose everything else. ### The permissions you can add | Group | Permission | What it allows | | --- | --- | --- | | **Project** | Create apps | Add new apps to the project | | | Create threads | Start new Build chats in the project | | | Rename project | Change the project's name | | | Delete project | Permanently delete the project | | **Secrets & integrations** | Manage integrations | Connect and configure integrations | | | View secret names | See which secrets exist (names only) | | | Reveal secret values | See the value of a secret | | | Edit secrets | Add, change, and remove secrets | | **Deployments** | Approve deployments | Approve or reject deployments that need sign-off | | **Activity** | View audit log | See the project's activity history | ```md alert info # Some permissions come as a set Revealing or editing secret values requires seeing the secret names, so turning on **Reveal secret values** or **Edit secrets** also includes **View secret names**. ``` ## Permissions you can't delegate Two abilities stay with the built-in **Admin** role and can't be added to a custom role: - **Managing members** (adding people and changing their roles) - **Managing roles** (creating and editing roles) Either one amounts to admin control. If someone needs it, give them the Admin role. Billing isn't part of project roles either; it comes from a person's [organization role](/docs/ai/organization/roles-and-permissions/). ## Assigning a custom role A custom role appears in the role dropdown wherever you assign project roles: when [adding members](/docs/ai/organization/project-access/) and when changing an existing member's role. Each custom role shows its base level and how many permissions it adds. ## Editing and deleting roles From the Roles tab, **edit** a custom role to rename it or change its permissions. Built-in roles can't be edited and carry a **Built-in** badge. To **delete** a custom role, first reassign any members using it to another role. Reflex won't delete a role while members are still assigned to it. ## Related - [Managing project access](/docs/ai/organization/project-access/) — assign a role to members. - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — how the built-in roles are made up. # Deployment Approvals Source: http://localhost:3000/docs/ai/organization/deployment-approvals.md --- tags: Organization description: Require deployments in a Reflex project to be approved before they go live, and manage the approval queue. --- ```python exec import reflex as rx ``` You can require every deployment in a project to be **approved** before it runs. With approvals on, anyone can request a deployment, but it waits for sign-off from someone with permission. This gives the team a checkpoint before changes reach production. Deployment approvals are configured per project, under **Settings → Deploy approvals**. ## Turning on approvals On the **Deploy approvals** tab, switch on **Require approval to deploy**. Deployments in the project are then held until approved. Only **project admins** can change this setting; others can see it but not change it. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/deployment-approvals/approval_policy.webp", alt="The Deploy approvals tab with the Require approval to deploy toggle switched on", class_name="rounded-md h-auto", ) ``` ## Who can approve Anyone with the **Approve deployments** permission can approve a deployment: - **Project admins**, who have it by default. - Anyone with a [custom role](/docs/ai/organization/custom-roles/) that grants **Approve deployments**. This separates who can request a deployment from who can approve one. For example, editors can deploy to staging while a release manager signs off on production. ## The approval flow 1. A team member starts a deployment as usual. 2. With approvals on, the deployment is held instead of running. 3. It appears under **Pending deployments** on the **Deploy approvals** tab, showing the app, who requested it, and where it would deploy (provider, region, and machine size). 4. An approver selects **Approve** to run it, or **Reject** to stop it. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/deployment-approvals/pending_deployments.webp", alt="A pending deployments list showing an app, the requester, deployment details, and Approve and Reject buttons", class_name="rounded-md h-auto", ) ``` ## Related - [Custom roles](/docs/ai/organization/custom-roles/) — grant the Approve deployments permission. - [Audit logs](/docs/ai/organization/audit-logs/) — review approvals and other activity. # Verified Domains & Auto-Join Source: http://localhost:3000/docs/ai/organization/domains.md --- tags: Organization description: Verify a company email domain so teammates automatically join your Reflex organization. --- ```python exec import reflex as rx ``` Inviting people one at a time works for small teams. For a whole company, verify your email domain (such as `acme.com`) so anyone with a matching address joins the organization automatically, without an individual invitation. Verified domains are managed under **Settings → Domains** and are part of the **Enterprise** plan. ## How it works 1. Claim a domain your company owns. 2. Prove ownership by adding a DNS record. 3. Once verified, people with an email on that domain join automatically, including both new sign-ups and existing Reflex users. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/domains/domains_list.webp", alt="The Domains tab showing a verified domain with an auto-join toggle and a pending domain awaiting verification", class_name="rounded-md h-auto", ) ``` ## Claiming and verifying a domain Select **Add domain** and enter the domain to claim, such as `acme.com`. It appears with a **Pending** status and a set of DNS instructions. To verify ownership, add the **TXT record** shown (its Type, Name, and Value) with your DNS provider, where you manage your domain. Then return and select **Verify**. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/domains/dns_verification.webp", alt="A pending domain card showing the TXT record type, name, and value to add, with a Verify button", class_name="rounded-md h-auto", ) ``` ```md alert info # DNS changes take a little time After you add the record, it can take a few minutes to propagate. If verification fails at first, wait and try again. ``` Only one organization can verify a given domain. ## Auto-join Once a domain is verified, **auto-join** is on by default: - **New people** who sign up with an email on the domain join the organization automatically. - **Existing Reflex users** with a matching email are added too. Turn auto-join **off** with the toggle to stop adding new people while keeping the domain verified. Use this to pause onboarding without removing the domain. ### Syncing existing members If a domain shows **Sync incomplete**, some existing users on it haven't been added yet. Select **Sync members** to add them; it's safe to run at any time. ### When you're out of seats If auto-join would add someone but the organization has no free [seats](/docs/ai/organization/members/), they go into the **Awaiting a seat** queue and hold no seat until an admin activates them. See [Members & seats](/docs/ai/organization/members/) to activate them. ## Removing a domain Select the delete icon on a domain's card and confirm. New people on that domain stop joining automatically, but existing members keep their access. You can claim the domain again later. ## Domains and single sign-on A verified domain is also required for [single sign-on](/docs/ai/organization/sso/). To have your team sign in through your identity provider, verify the domain first, then set up SSO. The Single sign-on card sits on this page, below your domains. ## Related - [Single sign-on (SSO)](/docs/ai/organization/sso/) — sign in through your identity provider. - [Members & seats](/docs/ai/organization/members/) — seats and the awaiting-a-seat queue. # Members & Seats Source: http://localhost:3000/docs/ai/organization/members.md --- tags: Organization description: Add people to a Reflex organization, manage invitations, and understand how seats are counted. --- ```python exec import reflex as rx ``` An organization's **members** are the people who can work in it. Once someone is a member, you can add them to specific projects. Manage members under your organization's **Settings → Team** tab. ## Seats Each member uses one **seat**. A pending invitation also holds a seat until the person joins or you revoke it. The Members tab shows how many seats are in use, such as *4 of 10 seats used*. When seats run out, remove a member, revoke an invitation, or add seats to your plan. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/members/members_list.webp", alt="The organization Members tab showing the seat counter and a list of members with their roles", class_name="rounded-md h-auto", ) ``` ```md alert info # Seats and billing Your seat count is part of your plan; see [Billing](/docs/hosting/billing/) to review or change it. If you're out of seats or don't see the option to add members, [contact sales](https://reflex.dev/pricing/). ``` ## Adding a member Select **Add member**, enter an email address, and choose the [organization role](/docs/ai/organization/roles-and-permissions/) the person should have. - If they already have a Reflex account, they're added right away. - If they don't, Reflex emails them an invitation, and they join once they sign up. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/members/add_member_dialog.webp", alt="The Add member dialog with fields for an email address and an organization role", class_name="rounded-md h-auto", ) ``` ```md alert info # Organization membership is not project access Adding someone to the organization doesn't give them access to any project. Grant that separately from each project's settings. See [Managing project access](/docs/ai/organization/project-access/). ``` ## Pending invitations Invited people who haven't joined appear under **Pending invitations**, with the role they'll receive. Each pending invitation holds a seat until it's accepted. To withdraw an invitation, select it and choose **Revoke**. You can re-invite the person later. ## Awaiting a seat With [verified domains](/docs/ai/organization/domains/), people whose email matches your domain join automatically. If the organization is out of seats when that happens, they go into an **Awaiting a seat** queue. Queued people hold no seat and have no access. When a seat frees up, select **Activate** to admit them. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/members/awaiting_seat.webp", alt="The Awaiting a seat section listing a user who joined by verified domain, with an Activate button", class_name="rounded-md h-auto", ) ``` ## Changing a member's role A member's **organization role** appears next to their name. Pick a new role from the dropdown to change it. See [Roles & permissions](/docs/ai/organization/roles-and-permissions/) for what each role can do. You can't change your own role; ask another admin if it needs to change. ## Removing a member Select the delete icon next to a member and confirm. They lose access to the organization and all of its projects. You can add them back later. ## Related - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — what each role can do. - [Managing project access](/docs/ai/organization/project-access/) — add members to projects. - [Verified domains & auto-join](/docs/ai/organization/domains/) — automatic joining by email domain. # Moving Projects & Apps Source: http://localhost:3000/docs/ai/organization/moving-projects-and-apps.md --- tags: Organization description: Move a Reflex project to another organization, or move an app to another project, as your team's structure changes. --- ```python exec import reflex as rx ``` Reflex lets you move work as your team's structure changes: transfer a project to another organization, or move an app to another project. ## Moving a project to another organization You can transfer a whole project, including its apps and settings, from one organization to another. This is useful when a project outgrows a personal organization or moves between teams. Open the project's **Settings → General** and find the **Move project** card. Choose the destination organization and confirm. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/moving/move_project.webp", alt="The Move project dialog with a dropdown to select a destination organization and a warning about members losing access", class_name="rounded-md h-auto", ) ``` ### What you need You must be an **admin of both organizations**: the current one and the destination. If you can't move a project, the card explains why: - You're not an admin of the project's current organization, or - You aren't an admin of any other organization to move it into. ### What happens to members A project's members come from its current organization. When you move it, **members who aren't in the destination organization lose access.** Reflex lists those people before you confirm. To keep someone's access, add them to the destination organization first. ```md alert warning # Check who loses access before moving Moving a project isn't undone automatically; you'd have to move it back. Review who loses access before confirming, and add anyone who should keep it to the destination organization first. ``` ## Moving an app to another project You can move an app from one project to another **within the same organization**, for example to group it with related apps. In Reflex Build, open the app's options menu (the **⋯** menu on the app), choose **Move app**, and pick the destination project. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/moving/move_app.webp", alt="The Move app dialog listing other projects in the same organization to move the app into", class_name="rounded-md h-auto", ) ``` Notes: - You can only move an app within the same organization. To move it across organizations, move the whole project instead. - You need permission to **create apps** in the destination project. - Integrations are connected per project, so you may need to reconnect them in the destination project after the move. ## Related - [Managing project access](/docs/ai/organization/project-access/) — add members to a project before moving it. - [Members & seats](/docs/ai/organization/members/) — add people to the destination organization. # Organizations Source: http://localhost:3000/docs/ai/organization/overview.md --- tags: Organization description: How organizations work in Reflex, and the projects, apps, members, roles, billing, and credits they contain. --- ```python exec import reflex as rx ``` An **organization** is a shared workspace in Reflex. It contains your projects and their apps, the people who work on them, the roles that control what each person can do, and your billing and credits. Every project and app belongs to an organization, in both [Reflex Build](/docs/ai/overview/what-is-reflex-build/) and [Reflex Cloud](/docs/hosting/deploy-quick-start/). Your account starts with one, created the first time you sign in. ## Organizations, projects, and apps Reflex has three levels. ```python eval rx.image( src=rx.color_mode_cond( "https://web.reflex-assets.dev/docs-preview/organization/overview/hierarchy.webp", "https://web.reflex-assets.dev/docs-preview/organization/overview/hierarchy_dark.webp", ), alt="Diagram showing an organization containing projects, and each project containing apps", class_name="rounded-md h-auto", ) ``` - **Organization**: the top level, usually one per company or team. Members, seats, billing, credits, verified domains, and single sign-on are set here. - **Project**: a group of related apps within an organization, with its own members, roles, and settings. Teams commonly use one project per product or client. - **App**: an application you build and deploy. Each app belongs to a project. People join the organization first, then get access to individual projects. This keeps one workspace usable for a large team without giving everyone access to everything in it. ## Where settings live Settings live at either the organization or the project level. | Organization level | Project level | | --- | --- | | Members and seats | Which members can open the project | | Organization roles (Admin, Manager, Member) | Project roles (Viewer, Editor, Admin, and custom roles) | | Billing, credits, and spending limits | Project name and deletion | | Verified domains and auto-join | Deployment approvals | | Single sign-on (SSO) | Secrets and integrations | | Connected cloud providers | Project activity (audit log) | | Organization-wide activity (audit log) | Apps and deployments | ## Creating an organization Most teams need only one organization. Create a second when you want fully separate members, billing, and projects, for example to keep two companies or clients apart. Open the **organization switcher** at the top of the page, choose **Create organization**, name it, and confirm. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/overview/create_organization.webp", alt="The organization switcher menu open, with the Create organization option highlighted", class_name="rounded-md h-auto", ) ``` ```md alert info # Creating organizations depends on your plan If you don't see the option to create one, it isn't included in your current plan. [Contact sales](https://reflex.dev/pricing/) to learn more. ``` ## Switching organizations If you belong to more than one organization, use the **organization switcher** to move between them. The projects, members, billing, and settings you see always belong to the selected organization. If a project or teammate appears to be missing, check that the right organization is selected. ## Name and ID The **General** tab of your organization's settings shows two fields: - **Organization name**: the display name used throughout Reflex. Admins can change it at any time. - **Organization ID**: a fixed identifier used by the CLI and API. You can copy it, but not change it. ## Deleting an organization Deleting an organization is permanent and removes every project inside it. Open **Settings → General** and use **Delete organization**. You'll be asked to type the organization's name to confirm. ```md alert warning # You can't delete your only organization Reflex won't delete an organization if it's the only one you belong to. Create or join another first. ``` ## Plans Verified domains, single sign-on, bring-your-own-cloud, and inviting teammates are Enterprise features, and your seat count depends on your plan. When a feature isn't part of your plan, Reflex shows a note with a link to [contact sales](https://reflex.dev/pricing/). ## In this section Getting your team in: - [Members & seats](/docs/ai/organization/members/) — add people and manage seats. - [Verified domains & auto-join](/docs/ai/organization/domains/) — let teammates join by email domain. - [Single sign-on (SSO)](/docs/ai/organization/sso/) — sign in through your identity provider. Controlling access: - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — what each organization and project role can do. - [Managing project access](/docs/ai/organization/project-access/) — add members to specific projects. - [Custom roles](/docs/ai/organization/custom-roles/) — define a role with the exact permissions you need. Governing and organizing: - [Deployment approvals](/docs/ai/organization/deployment-approvals/) — require sign-off before a deployment runs. - [Audit logs](/docs/ai/organization/audit-logs/) — review who did what. - [Moving projects & apps](/docs/ai/organization/moving-projects-and-apps/) — move work between projects and organizations. - [Bring your own cloud](/docs/ai/organization/cloud-providers/) — run apps on your own infrastructure. # Managing Project Access Source: http://localhost:3000/docs/ai/organization/project-access.md --- tags: Organization description: Give organization members access to specific Reflex projects and assign each person a project role. --- ```python exec import reflex as rx ``` Organization membership lets someone join projects, but not any specific one. You control access per project: who can open it and what they can do. Manage this under each project's **Settings → Members** tab. ## Who already has access Some people have access without being added: - **Organization admins** can open every project. They show an **Organization admin** badge in the member list, and their access can't be changed here because it comes from their organization role. - **Managers and members** have no access to a project until you add them. ## Adding members to a project Select **Add user** to open the member picker. It lists organization members who aren't on the project yet. Choose one or more, assign each a [project role](/docs/ai/organization/roles-and-permissions/), and select **Add to project**. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/project-access/add_project_members.webp", alt="The Add project members dialog showing selectable organization members and a role dropdown for each", class_name="rounded-md h-auto", ) ``` You can only add people who already belong to your organization. If someone isn't in the list, they aren't a member yet. ```md alert info # Can't find someone? They need to join the organization first. If you're an admin, add them under the organization's **Settings → Team** (see [Members & seats](/docs/ai/organization/members/)); otherwise, ask an admin to add them. ``` ## Changing a member's project role Each member has a role dropdown next to their name. Pick a new role to change their access. Two cases can't be edited here: - **Your own role**, so you can't lock yourself out. - **Organization admins**, whose access is inherited from the organization. Assigning project roles requires the project **Admin** role or organization admin. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/project-access/project_member_roles.webp", alt="A project member list with a role dropdown open, and an organization admin shown with a non-editable badge", class_name="rounded-md h-auto", ) ``` ## Removing a member from a project Select the delete icon next to a member and confirm. They lose access to this project but remain in the organization and keep access to their other projects. To remove someone from the organization entirely, use the organization's Members tab instead (see [Members & seats](/docs/ai/organization/members/)). ```md alert info # Inviting teammates is an Enterprise feature Collaborating with teammates on projects is part of the Enterprise plan. If you don't see the option to add members, [contact sales](https://reflex.dev/pricing/). ``` ## Related - [Roles & permissions](/docs/ai/organization/roles-and-permissions/) — what each project role can do. - [Custom roles](/docs/ai/organization/custom-roles/) — define a role with specific permissions. # Roles & Permissions Source: http://localhost:3000/docs/ai/organization/roles-and-permissions.md --- tags: Organization description: How Reflex roles and permissions work at the organization and project level, and which role to give each person. --- ```python exec import reflex as rx ``` Roles decide what each person can do. Reflex has two levels of roles: - **Organization roles** apply across the whole organization: managing members, billing, and creating projects. - **Project roles** apply within a single project: building apps, editing secrets, and approving deployments. The two are assigned separately. Organization roles govern running the organization; project roles govern the work done inside a project. ## Organization roles Every member has one organization role. | | **Member** | **Manager** | **Admin** | | --- | :---: | :---: | :---: | | Belong to the organization and use the projects they're added to | ✓ | ✓ | ✓ | | Create new projects | | ✓ | ✓ | | View the organization audit log | | ✓ | ✓ | | Add and remove members, change roles | | | ✓ | | Rename or delete the organization | | | ✓ | | Manage billing, seats, and credits | | | ✓ | | Verify domains and configure single sign-on | | | ✓ | | Connect cloud providers | | | ✓ | | Automatically an **Admin** of every project | | | ✓ | - **Member**: has access only to the projects they're added to, with the project role they're given there. - **Manager**: a member who can also create projects. Suited to team leads who don't manage people or billing. - **Admin**: manages members and billing, and is an admin of every project in the organization. ```md alert info # Organization admins have access to every project An organization admin is a project admin everywhere, so you don't add them to projects individually. They appear in a project's member list with an **Organization admin** badge, and their access can't be edited there. ``` ## Project roles When you add a member to a project, you assign a **project role**. Reflex has three built-in roles, plus [custom roles](/docs/ai/organization/custom-roles/) for cases the built-ins don't cover. | | **Viewer** | **Editor** | **Admin** | | --- | :---: | :---: | :---: | | View the project, its apps, and activity | ✓ | ✓ | ✓ | | Create apps | | ✓ | ✓ | | Create threads (Build chats) | | ✓ | ✓ | | See that secrets exist (view names) | | ✓ | ✓ | | Reveal secret values | | | ✓ | | Add and edit secrets | | | ✓ | | Manage integrations | | | ✓ | | Approve deployments | | | ✓ | | View the project audit log | | | ✓ | | Rename the project | | | ✓ | | Delete the project | | | ✓ | | Add and remove project members, assign roles | | | ✓ | | Create and edit custom roles | | | ✓ | - **Viewer**: read-only. Can see the project and its apps but can't edit, deploy, or reveal secret values. - **Editor**: creates and edits apps, works in Build, and manages app secrets. Can see that project secrets exist but not reveal their values. - **Admin**: full control of the project, including members, roles, integrations, secrets, approvals, and deletion. ## How the two levels combine A person's access to a project comes from either their organization role or their project role: - **Organization admins** are admins of every project automatically; you don't add them. - **Managers and members** have no project access until you add them to a project and assign a role. See [Managing project access](/docs/ai/organization/project-access/). - Changing someone's project role doesn't change their organization role, and the reverse is also true. ```python eval rx.image( src=rx.color_mode_cond( light="https://web.reflex-assets.dev/docs-preview/organization/roles-and-permissions/roles_overview.webp", dark="https://web.reflex-assets.dev/docs-preview/organization/roles-and-permissions/roles_overview_dark.webp", ), alt="Diagram showing organization roles on the left and project roles on the right, with an organization admin inheriting project admin across all projects", class_name="rounded-md h-auto", ) ``` ## Choosing a role Guidelines for most teams: - Give organization **Admin** only to the few people who manage the team and its billing. - Use **Manager** for team leads who create projects but shouldn't manage people or billing. - Keep most people as **Member** and control their access project by project. - Within a project, make builders **Editors**, reserve **Admin** for project owners, and use **Viewer** for anyone who only needs to view. - When a built-in project role is close but not exact, create a [custom role](/docs/ai/organization/custom-roles/) rather than over-granting Admin. ## Permission reference The built-in project roles are combinations of individual permissions, which you can also assemble into a [custom role](/docs/ai/organization/custom-roles/). Reflex groups them as: - **Project:** Create apps, Create threads, Rename project, Delete project. - **Secrets & integrations:** Manage integrations, View secret names, Reveal secret values, Edit secrets. - **Deployments:** Approve deployments. - **Activity:** View audit log. **Managing members** and **managing roles** can't be granted on their own. Either one amounts to admin control, so both stay with the Admin role. Billing permissions come from your organization role, not from project roles. ## Related - [Managing project access](/docs/ai/organization/project-access/) — add members to a project and assign roles. - [Custom roles](/docs/ai/organization/custom-roles/) — define a role with specific permissions. # Single Sign-On (SSO) Source: http://localhost:3000/docs/ai/organization/sso.md --- tags: Organization description: Set up single sign-on (SSO) so your team signs in to Reflex through your company identity provider using SAML or OIDC. --- ```python exec import reflex as rx ``` **Single sign-on (SSO)** lets your team sign in to Reflex through your company's identity provider, using the same login as your other work tools. People don't manage separate Reflex credentials, and your IT team controls access from one place. Reflex supports **SAML** and **OIDC**, so it works with providers such as Okta, Microsoft Entra ID, and Google Workspace. SSO is set up on the **Domains** page (Settings → Domains), where it appears as a Single sign-on card once a domain is verified. It's part of the **Enterprise** plan. ```python eval rx.image( src="https://web.reflex-assets.dev/docs-preview/organization/sso/sso_card.webp", alt="The Single sign-on settings card showing an Enable SSO button and status badge", class_name="rounded-md h-auto", ) ``` ## Before you begin SSO requires a [verified domain](/docs/ai/organization/domains/). Reflex uses the domain to route the right sign-ins to your provider, so verify it first. ## Setting up SSO 1. Verify your domain (see [Verified domains & auto-join](/docs/ai/organization/domains/)). 2. On the Domains page, find the **Single sign-on** card and select **Enable SSO**. 3. Select **Configure identity provider** and follow the steps to connect your provider and exchange configuration details. Once configured, the card shows an **Enabled** badge, and members on your verified domain sign in through your provider. ```md alert info # Who can set up SSO Only organization admins can enable, configure, or disable single sign-on. ``` ## How members sign in With SSO enabled, people on your verified domain are sent to your provider to sign in. Access is governed there, so removing someone in your provider also removes their access to Reflex. ## Disabling SSO Use **Disable** on the Single sign-on card. Members go back to signing in with their usual method, and your provider configuration is removed from Reflex. Disabling SSO doesn't remove anyone; existing members keep their access. You can disable SSO even after a plan change, so your organization is never locked into a provider. ## Related - [Verified domains & auto-join](/docs/ai/organization/domains/) — the prerequisite for SSO. - [Members & seats](/docs/ai/organization/members/) — manage who belongs to the organization. # Reflex Build: Best Practices Source: http://localhost:3000/docs/ai/overview/best-practices.md > A comprehensive guide to working effectively with AI Builder. This guide outlines how to get the most reliable and efficient results when working with the AI Builder inside Reflex Build. The key to success is clarity, structure, and iteration. --- ## Core Workflow ### Foundation: Planning Before You Build Before jumping into the AI Builder, take time to plan your approach. Good preparation leads to better results and fewer iterations. - **Define your core purpose and users** — Write a 2-3 sentence app description and identify your target users and their needs. - **Prioritize 3-5 key features** — Focus on the most important functionality first, then expand from there. - **Gather visual references** — Collect screenshots, wireframes, or sketches of layouts you want to emulate. - **Structure your data** — List what information each page needs to display and how users will interact with it. - **Start with your most important page** — Usually your main dashboard, home screen, or primary workflow. Get this right first. ### Auto-Generate Prompts from App Specs To save time and get higher-quality prompts, you can feed your full app spec into the AI Builder and ask it to break the spec into structured, build-ready prompts. The AI Builder can translate your vision into: - Layout instructions - UI component definitions - Data model requirements - Styling preferences - Follow-up test plans **Example workflow:** 1. Paste your full app specification into the AI Builder 2. Ask **"Break this into a series of buildable prompts."** 3. Execute each generated prompt in sequence 4. Build iteratively using the structured prompts ### Working with Text Specifications If you have a structured app specification, don't paste the entire document into the builder at once. Break it down into logical sections and feed them in sequence. **Pro tip:** Let the AI Builder help prepare prompts: Paste your full app spec and ask: - **"Break this into buildable prompts."** - **"Write one prompt per feature/page to build this app."** This approach — planning first, then building iteratively — lets you move faster and build smarter. ### Writing Clear, Task-Oriented Prompts The AI performs best when it receives **specific, outcome-driven instructions**. Avoid vague, broad prompts. ```diff - Build me an admin dashboard. # <- Bad + Create a 2-column layout with a sidebar for navigation and a top navbar. # <- Good ``` Whenever possible, split large tasks into smaller steps: - Define the layout first (columns, rows, sidebar) - Add UI components (buttons, inputs, modals) - Handle data models and states later - Use follow-ups to style and polish Use precise styling language, for example: ```diff - Grid items have small spacing and sharp corners. + Add medium spacing between grid items and use large rounded corners on cards. ``` Avoid subjective terms like "**nice**," "**modern**," or "**clean**." Treat your prompt as interface documentation for the builder. ### Working with Images and Visual References You can drop in screenshots of websites, dashboards, apps, or even hand-drawn wireframes. The builder will extract layout, design, and functionality ideas from these images. **Tips for images:** - Clear screenshots work best - Include any elements you want: forms, tables, nav, charts - You can annotate them with arrows, notes, or labels **Get UI/UX feedback:** Upload a screenshot and ask: **"What are 5 things I could do to improve the UI/UX of this?"** Follow up with: **Implement items 1, 2, and 4.** Or request specific improvements: **Make this more minimal and mobile-first.** --- ## Optimizing Your Workflow ### Building Iteratively Trying to generate your full app in a single prompt almost never works well. Instead, approach your build in clear stages: 1. **Layout** — Grid, Flex, responsive columns/rows 2. **Components** — Tables, buttons, modals, charts, etc. 3. **State** — Bindings, stores, mock data 4. **Refinement** — Tweaks, visual polish, edge case handling At each stage, give feedback and iterate. If the AI builder makes something close, you can say: ```diff - Modal is included and sidebar is static. + Remove modal and make sidebar collapsible. - Buttons vary across pages. + Use same button style from home page. - Card layout differs across sections. + Repeat card layout from dashboard section. ``` ### Improving UI/UX To improve your design, ask the builder for more polished layouts, better structure, or more modern styles. ```diff - Layout feels cluttered with small headings and dense sections. + Increase heading sizes and spacing between sections for better hierarchy. - Navigation buttons are inconsistent. + Standardize button sizes and colors for consistent UX. ``` **Suggested workflow:** 1. Upload an image or describe the layout. 2. Ask: **"Tell me 5 things that would improve the UI/UX of this page."** 3. Review the suggestions and decide which ones you want to apply. 4. Implement: **"Improve visual hierarchy by increasing heading sizes and adding more spacing between sections."** ### Using Knowledge to Guide the Build The **Knowledge** panel lets you provide long-form references that influence how the agent builds your app. Add design systems, style guides, brand guidelines, or architecture rules. Once added, the builder will try to honor these rules throughout the session, ensuring consistency without repeating instructions. Try combining Knowledge with your prompts: - **Use the style guide in Knowledge to improve this page.** - **Is the current layout aligned with our design system in Knowledge?** ### Local Development Integration We have an MCP server available for enterprise customers to connect local AI development tools such as Claude Desktop, Windsurf, or Codex. This enables a hybrid workflow: generate your app and make major changes in the App Builder, then move to local development for detailed refinements and custom functionality. > **Enterprise Feature:** The Reflex MCP integration is currently only available for enterprise customers. [Book a demo](https://reflex.dev/pricing/) to discuss access. #### Quick Setup **Prerequisites:** - MCP-compatible AI tool (Claude Desktop, Windsurf, Codex) - Valid Reflex account for OAuth authentication - Internet connection to the hosted MCP server **Benefits:** - **Seamless handoff** — Move between web builder and local development - **AI-powered local development** — Use your preferred AI tools with Reflex projects - **No local installation** — Hosted MCP server requires no additional Python packages - **Secure authentication** — OAuth 2.1 integration with your Reflex account For complete setup instructions for Claude Desktop, Windsurf, Codex, and other MCP clients, visit our [MCP integration](https://reflex.dev/docs/ai/integrations/mcp-installation/) documentation. --- ## Key Takeaways - **Plan before you build**. A few minutes of preparation saves hours of iteration. - **Think modularly**. Focus on atomic parts before the full system. - **Write like a designer-developer**. Clear, structural, and functional language wins. - **Iterate continuously**. Let each prompt get you 80% there, then refine. With these techniques, the AI Builder becomes a reliable extension of your creative and technical intent. # Templates Source: http://localhost:3000/docs/ai/overview/templates.md Reflex has many certified templates, seen on the `Trending` tab of the Reflex Build, that can be used to kickstart your app. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/templates_light.avif", alt="Reflex AI Builder templates gallery", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## Using a Template To use a template, simply click the template and then in the bottom right corner of the app click the `Fork` button. This will create a copy of the template in your own account. You can then edit the app as you like with further prompting. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/fork_template_light.avif", alt="Forking a template in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` Templates are great to get started if they have similar UI to what you are looking to build. You can then add your own data to the app. # Your First Reflex Build App Source: http://localhost:3000/docs/ai/overview/tutorial.md In this tutorial, you'll build a data dashboard application that displays employee information in both table and chart formats, with interactive features for filtering and adding new data. We'll also add a simple chatbot page to demonstrate multi-page navigation. ## What You'll Build By the end of this tutorial, you'll have created: - A dashboard displaying employee data in a table and bar chart - Interactive filtering to search through your data - A modal form for adding new employees - A separate page with a simple chatbot interface This tutorial assumes you're starting with a new project in AI Builder. --- ## Creating Your Dashboard Let's start by building the core of our application - a dashboard that displays employee data. **Prompt:** ``` Create a dashboard page with a table showing sample employee data with columns: Name, Department, and Salary. Below the table, add a bar chart that visualizes the salary data. Include at least 5 sample employees with different departments and salary ranges. ``` This will create your main dashboard page with both tabular and visual representations of your data. The AI will generate sample employee records and create a bar chart that makes it easy to compare salaries across your team. ```python exec import reflex as rx ``` ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/tutorial_1_light.avif", alt="Creating a dashboard in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## Adding Interactive Filtering Now that you have your basic dashboard, let's make it more interactive by adding the ability to filter and search through your employee data. **Prompt:** ``` Add filtering functionality to the employee table. Include a search input above the table that filters rows based on name, and dropdown filters for department. Make sure the filters work together and update the table in real-time. ``` Your dashboard now becomes much more useful with real-time filtering. Users can quickly find specific employees by name or narrow down results by department. The filters work together, so you can combine a department filter with a name search. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/tutorial_2_light.avif", alt="Adding interactive filtering in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## Enabling Data Entry A static dashboard is useful, but being able to add new data makes your app much more practical. Let's add the ability to create new employee records. **Prompt:** ``` Add an "Add Employee" button above the table. When clicked, open a modal with input fields for Name, Department, and Salary. When the form is submitted, add the new employee to the table and update the bar chart. Include form validation for required fields. ``` Your app now has full CRUD capability for employee records. The modal form provides a clean interface for data entry, and both your table and chart update immediately when new employees are added. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/tutorial_3_light.avif", alt="Enabling data entry in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## Building a Multi-Page App Most real applications have multiple pages. Let's add a chatbot page to demonstrate navigation and create a more complete user experience. **Prompt:** ``` Create a new page called "Chat" and add it to the navigation. Build a simple chatbot interface with a message input field, send button, and chat history display. For now, make the bot echo back the user's messages with "Bot says: [user message]". ``` Your app now has proper navigation between the dashboard and chat functionality. The chatbot page demonstrates how easy it is to add new features and pages to your AI Builder application. ```python eval rx.el.div( rx.image( src="https://web.reflex-assets.dev/ai_builder/overview/tutorial_4_light.avif", alt="Building a multi-page app in Reflex AI Builder", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full flex flex-col rounded-md", ) ``` ## What's Next? You've successfully built a complete web application with data visualization, interactive filtering, data entry, and multi-page navigation. Your app demonstrates many common patterns used in modern web applications: - **Data presentation** with tables and charts - **User interaction** through filtering and forms - **Real-time updates** when data changes - **Multi-page architecture** with navigation ## Exploring Further Now that you have a working foundation, try experimenting with these ideas: - **Customize the data model** - change the employee fields or add new columns - **Enhance the visualizations** - try different chart types or add more charts - **Improve the chatbot** - give it more sophisticated responses or integrate it with your employee data - **Add more pages** - create additional features like employee profiles or reporting dashboards The power of AI Builder is that you can iterate quickly with natural language prompts. Each new feature is just a conversation away! # What Is Reflex Build Source: http://localhost:3000/docs/ai/overview/what-is-reflex-build.md Reflex Build is an AI-powered platform that lets anyone create full-stack web apps just by describing ideas in plain English—no coding needed. It includes a full-fledged built-in IDE, real-time collaboration, and project sharing—all in your browser, no installation required. ```python exec import reflex as rx landing_features = [ { "title": "Database Integration", "description": "Automatically integrate your database\ninto your application with ease", "icon": "database", }, { "title": "Secure Secrets", "description": "Safely manage your API keys and tokens\nwith a built in secrets manager", "icon": "shield", }, { "title": "Live Preview", "description": "See all application changes in real-time\nwith our interactive preview tab", "icon": "eye", }, { "title": "Quick Download", "description": "Download your complete project files\nwith just a single click operation", "icon": "download", }, { "title": "Easy Deployment", "description": "Deploy your application to production\nwith just a single click process", "icon": "rocket", }, { "title": "Manual File Editing", "description": "Edit your project files directly\nwith our intuitive code editor", "icon": "code", }, { "title": "AI Package Manager", "description": "Let AI handle your package installations\nvia natural prompting", "icon": "sparkles", }, { "title": "Smart Prompting", "description": "Get better development results\nwith AI-optimized prompt templates", "icon": "message-circle", }, ] features_data = [ { "title": "Project Menu Bar", "subtitle": "Browse previously built applications, create new sessions, store database variables, and much more!", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/project_bar_light.avif", }, { "title": "Chat Area", "subtitle": "See your prompts in action with visual cues, editing notifications, and file generations every step of the way.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/chat_light.avif", }, { "title": "Application Workspace", "subtitle": "Your workspace contains all the folders and files of your application. You can add new files and folders as well!", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/file_tree_light.avif", }, { "title": "Code Editor", "subtitle": "The code editor displays the current selected file. You can edit the code directly and save it instantly.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/code_light.avif", }, { "title": "Integrations", "subtitle": "Easily connect with the tools your team already uses or extend your app with any Python SDK, library, or API.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/integrations_light.avif", }, { "title": "Plan", "subtitle": "Plan your application's development with the AI Builder. You can add or remove phases and tasks as you go.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/plan_light.avif", }, { "title": "Top Menu Bar", "subtitle": "This menu contains the main views of the application. Preview, Code, Plan, Integrations, Knowledge, Secrets and Settings. You can also see the current workspace RAM and CPU usage. Deploy, copy or share your application with the buttons in the top right corner.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/top_light.avif", }, { "title": "Preview Tab", "subtitle": "The preview tab showcases a live application. You can navigate to other applications directly from this tab, refresh the app, and even view it in full screen.", "img": "https://web.reflex-assets.dev/ai_builder/what_is_reflex_build/preview_light.avif", }, ] def feature_card(feature: dict) -> rx.Component: return rx.el.div( rx.el.div( rx.el.span( rx.icon( tag=feature["icon"], size=15, class_name="inline-block mr-2 text-primary-11", ), rx.el.span(f"{feature['title']}"), class_name="text-sm font-semibold flex flex-row items-center pt-5 px-2 text-secondary-12", ), rx.el.span( feature["description"], class_name="text-sm font-medium block align-center px-2 text-secondary-11", ), class_name="flex flex-col gap-2", ), class_name="w-full rounded-md", ) def _docs_features() -> rx.Component: return rx.el.div( rx.el.div( rx.foreach(landing_features, feature_card), class_name="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 gap-4", ), class_name="flex flex-col w-full h-full justify-start align-start items-start py-4 gap-x-4 z-[99]", ) def _docs_app_section_features_small_screen(feature: dict): return rx.el.div( rx.image( src=feature["img"], alt="Reflex Build feature illustration", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), rx.el.div( rx.el.label( feature["title"], class_name="text-sm font-bold cursor-pointer" ), rx.el.label( feature["subtitle"], class_name="text-sm font-light cursor-pointer" ), class_name="flex flex-col px-1 py-2", ), class_name="w-full flex flex-col rounded-md", ) def _docs_app_section_toggles(feature: dict): return rx.el.div( rx.el.label(feature["title"], class_name="text-sm font-bold"), rx.el.label(feature["subtitle"], class_name="text-sm font-light"), class_name="w-full flex flex-col max-w-md rounded-md p-4", ) def _docs_app_sections(): return rx.el.div( rx.el.div( rx.el.div( rx.el.label( "Small details, big impact", class_name="text-sm font-light" ), rx.el.label( "Made With Exceptional Care", class_name="text-3xl font-bold" ), rx.el.label( "Every feature in Reflex Build is carefully crafted to set new standards. Mediocre isn't an option.", class_name="text-md font-regular", ), class_name="flex flex-col w-full max-w-lg gap-y-1", ), rx.foreach( features_data[:5], lambda feature: _docs_app_section_toggles(feature), ), class_name="flex flex-col gap-y-4 justify-start max-w-sm", ), rx.el.div( rx.image( src=features_data[0]["img"], alt="Reflex Build feature illustration", class_name="rounded-md h-auto", border=f"0.81px solid {rx.color('slate', 5)}", ), class_name="w-full max-w-4xl", ), class_name="flex flex-row w-full h-full justify-between align-center items-center py-4 gap-x-4 z-[99]", display=["none" if i <= 4 else "flex" for i in range(6)], ) def _docs_app_sections_small_screen(): return rx.el.div( rx.el.div( rx.grid( rx.foreach( features_data, lambda feature: _docs_app_section_features_small_screen(feature), ), class_name="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-10 w-full", ), class_name="flex flex-col gap-y-4 justify-start py-4", ), ) screen_normalization = "z-[99] w-full" ``` ## Feature Overview Reflex Build provides a streamlined interface for building AI applications. The **Project Menu Bar** helps you manage sessions and stored variables, while the **Chat Area** displays real-time prompts, edits, and file generations. The **Application Workspace** organizes your project structure, and the **Code Editor** allows direct, instant code editing. Key actions like deploy and share are accessible via the **Bottom Menu Bar**, and the **Preview Tab** lets you view and interact with your live app at any time. ```python eval rx.el.div( _docs_app_sections_small_screen(), ) ``` ## Interface Highlights Reflex Build’s interface is designed for clarity and efficiency. The **Project Menu Bar** helps you manage sessions, apps, and variables. The **Chat Area** shows prompts in action with visual feedback and file generation. In the **Application Workspace**, you can view and organize your project files. The **Code Editor** allows quick, direct edits with instant saving. Use the **Bottom Menu Bar** for key actions like deploy and download. The **Preview Tab** lets you interact with a live version of your app, including refresh and full-screen options. ```python eval rx.el.div( rx.el.div(_docs_features(), class_name=screen_normalization), ) ``` # Python Libraries Source: http://localhost:3000/docs/ai/python-libraries.md Not every service or tool has a first-class integration — but your app can still connect to **any Python library** directly. By leveraging the built-in Python runtime, the AI Builder can install packages, import libraries, and call their functions from workflows or components. This gives you maximum flexibility to extend your app with the broader Python ecosystem. When you ask for a certain functionality the AI Builder will first check if there is a `first-class integration` available. If not, it will `search the web` to try and find a relevant Python library to fulfill your request. If it finds one, it will install the package and ask you to set any `API keys` that are required. ## Example Use Cases ### Slack There is no built-in integration for Slack. But if you ask the AI Builder to send a message to a Slack channel, it will research itself the best implementation and then use the `slack_sdk` Python package to send the message. ### Scikit-learn There is no built-in integration for Scikit-learn. But if you ask the AI Builder to classify some text using scikit-learn, it will research itself the best implementation and then use the `scikit-learn` Python package to load a pre-trained model and classify the text. ## Adding Custom Knowledge If you are working with a specialized / less well-known library, you can add custom knowledge to help the AI Builder understand how to use it. Simply provide a brief description of the library, its purpose, and example usage in the **Knowledge** section of your app settings. This will guide the AI Builder when it attempts to call functions from that library. ```python exec import reflex as rx ``` ```md alert warning # Where to find the Knowledge Section ``` ```python eval rx.image( src="https://web.reflex-assets.dev/ai_builder/features/knowledge_light.avif", alt="Where to find the Knowledge Section", class_name="rounded-lg border border-secondary-a4 mb-2", ) ``` ## What is Not Supported There are a very small number of libraries that are not supported due to their size. For example, large machine learning frameworks like `torch` or `tensorflow` are not supported directly. In these cases, we recommend using a first-class integration that can emulate similar functionality (e.g., the Replicate integration for running ML models in the cloud). # URLs Source: http://localhost:3000/docs/ai/urls.md When you **paste a URL directly into the AI Builder chat**, the AI will automatically decide how to handle it depending on your prompt. You can use URLs to **copy a page’s design** or **extract its content**, without needing to set up any integration. ## How It Works * If you say something like **“copy the design”** or **“use this layout”**, the AI will: * Take a **screenshot** of the page. * Use it as a **visual reference** to recreate the UI in your app. * Allow you to **customize** the generated design afterward. * 🪄 If you say something like **“get the content”**, **“scrape this page”**, or just paste the URL without mentioning design, the AI will: * **Scrape the content** of the page (text, links, images, metadata). * Return it as structured data that can be used in components, workflows, or AI actions. ## Example Prompts * “Copy the design of this page.” * “Scrape the content from this blog post.” * “Get all the product details from this URL.” * (Paste the URL alone) → AI will assume content scraping by default. ## Notes * **Public pages only:** The AI can only process URLs that are publicly accessible. * **Editable:** Both the generated design and scraped content can be modified after processing. # Webhooks Source: http://localhost:3000/docs/ai/webhooks.md Webhooks allow your app to **send data to external services** in real time. You provide the AI Builder with a **webhook URL** created in another platform, and it can **automatically send payloads** to that URL when workflows are triggered. This is a simple and powerful way to integrate with services that support incoming webhooks, even if there’s no first-class integration. ## What You Can Do With outgoing webhooks, your app can: * **Send structured payloads** to any service that supports incoming webhooks (e.g., Slack, Zapier, Make, Discord). * **Trigger external workflows** when events happen in your app. * **Push real-time updates** to third-party systems without writing custom backend code. * Chain webhook calls with other integrations or AI actions. ## Step 1: Create a Webhook in the External Service 1. Go to the external service you want to connect (e.g., Slack, Zapier, Discord, Make). 2. Create a new **incoming webhook** in that service. 3. Copy the **webhook URL** it provides. ## Step 2: Add the Webhook URL to AI Builder 1. In the AI Builder chat paste the **webhook URL** you created in the external service. 2. You can then instruct the AI to **send data to this URL** whenever a workflow is triggered. 3. Optionally define the **payload structure** (e.g., JSON body) and when it should be sent. > 💡 Example: “Send user signup data to this webhook whenever a user signs up.” ## Step 3: Sending Payloads * The AI Builder will write the code to send a `POST` request to the webhook URL with the payload you define. * The payload can include **dynamic data** from your app — such as user info, state variables, or model outputs. * You can trigger these webhook calls from buttons, events, workflows, or automations. ## Step 4: Notes * **Security:** Only use webhook URLs from trusted services — they will receive your data as-is. * **Formatting:** Make sure the payload matches the expected format of the external service. * **Chaining:** You can use multiple webhooks or combine them with other integrations. * **Use cases:** Slack alerts, CRM updates, triggering automations in Zapier or Make, notifying custom systems. # Browser Javascript Source: http://localhost:3000/docs/api-reference/browser-javascript.md ```python exec import asyncio from typing import Any import reflex as rx ``` Reflex compiles your frontend code, defined as python functions, into a Javascript web application that runs in the user's browser. There are instances where you may need to supply custom javascript code to interop with Web APIs, use certain third-party libraries, or wrap low-level functionality that is not exposed via Reflex's Python API. ```md alert # Avoid Custom Javascript Custom Javascript code in your Reflex app presents a maintenance challenge, as it will be harder to debug and may be unstable across Reflex versions. Prefer to use the Python API whenever possible and file an issue if you need additional functionality that is not currently provided. ``` ## Executing Script There are four ways to execute custom Javascript code into your Reflex app: - `rx.script` - Injects the script via `next/script` for efficient loading of inline and external Javascript code. Described further in the [component library](/docs/library/other/script). - These components can be directly included in the body of a page, or they may be passed to `rx.App(head_components=[rx.script(...)])` to be included in the `` tag of all pages. - `rx.call_script` - An event handler that evaluates arbitrary Javascript code, and optionally returns the result to another event handler. These previous two methods can work in tandem to load external scripts and then call functions defined within them in response to user events. The following two methods are geared towards wrapping components and are described with examples in the [Wrapping React](/docs/wrapping-react/overview) section. - `_get_hooks` and `_get_custom_code` in an `rx.Component` subclass - `Var.create` with `_var_is_local=False` ## Inline Scripts The `rx.script` component is the recommended way to load inline Javascript for greater control over frontend behavior. The functions and variables in the script can be accessed from backend event handlers or frontend event triggers via the `rx.call_script` interface. ```python demo exec class SoundEffectState(rx.State): @rx.event(background=True) async def delayed_play(self): await asyncio.sleep(1) return rx.call_script("playFromStart(button_sfx)") def sound_effect_demo(): return rx.hstack( rx.script(""" var button_sfx = new Audio("/vintage-button-sound-effect.mp3") function playFromStart (sfx) {sfx.load(); sfx.play()}"""), rx.button( "Play Immediately", on_click=rx.call_script("playFromStart(button_sfx)") ), rx.button("Play Later", on_click=SoundEffectState.delayed_play), ) ``` ## External Scripts External scripts can be loaded either from the `assets` directory, or from CDN URL, and then controlled via `rx.call_script`. ```python demo rx.vstack( rx.script( src="https://cdn.jsdelivr.net/gh/scottschiller/snowstorm@snowstorm_20131208/snowstorm-min.js", ), rx.script(""" window.addEventListener('load', function() { if (typeof snowStorm !== 'undefined') { snowStorm.autoStart = false; snowStorm.snowColor = '#111'; } }); """), rx.button("Start Duststorm", on_click=rx.call_script("snowStorm.start()")), rx.button("Toggle Duststorm", on_click=rx.call_script("snowStorm.toggleSnow()")), ) ``` ## Accessing Client Side Values The `rx.call_script` function accepts a `callback` parameter that expects an Event Handler with one argument which will receive the result of evaluating the Javascript code. This can be used to access client-side values such as the `window.location` or current scroll location, or any previously defined value. ```python demo exec class WindowState(rx.State): location: dict[str, str] = {} scroll_position: dict[str, int] = {} def update_location(self, location): self.location = location def update_scroll_position(self, scroll_position): self.scroll_position = { "x": scroll_position[0], "y": scroll_position[1], } @rx.event def get_client_values(self): return [ rx.call_script("window.location", callback=WindowState.update_location), rx.call_script( "[window.scrollX, window.scrollY]", callback=WindowState.update_scroll_position, ), ] def window_state_demo(): return rx.vstack( rx.button("Update Values", on_click=WindowState.get_client_values), rx.text(f"Scroll Position: {WindowState.scroll_position.to_string()}"), rx.text("window.location:"), rx.text_area(value=WindowState.location.to_string(), is_read_only=True), on_mount=WindowState.get_client_values, ) ``` ```md alert # Allowed Callback Values The `callback` parameter may be an `EventHandler` with one argument, or a lambda with one argument that returns an `EventHandler`. If the callback is None, then no event is triggered. ``` ## Using React Hooks To use React Hooks directly in a Reflex app, you must subclass `rx.Component`, typically `rx.Fragment` is used when the hook functionality has no visual element. The hook code is returned by the `add_hooks` method, which is expected to return a `list[str]` containing Javascript code which will be inserted into the page component (i.e the render function itself). For supporting code that must be defined outside of the component render function, use `_get_custom_code`. The following example uses `useEffect` to register global hotkeys on the `document` object, and then triggers an event when a specific key is pressed. ```python demo exec import dataclasses from reflex.utils import imports @dataclasses.dataclass class KeyEvent: """Interface of Javascript KeyboardEvent""" key: str = "" def key_event_spec(ev: rx.Var[KeyEvent]) -> tuple[rx.Var[str]]: # Takes the event object and returns the key pressed to send to the state return (ev.key,) class GlobalHotkeyState(rx.State): key: str = "" @rx.event def update_key(self, key): self.key = key class GlobalHotkeyWatcher(rx.Fragment): """A component that listens for key events globally.""" # The event handler that will be called on_key_down: rx.EventHandler[key_event_spec] def add_imports(self) -> imports.ImportDict: """Add the imports for the component.""" return { "react": [imports.ImportVar(tag="useEffect")], } def add_hooks(self) -> list[str | rx.Var]: """Add the hooks for the component.""" return [ """ useEffect(() => { const handle_key = %s; document.addEventListener("keydown", handle_key, false); return () => { document.removeEventListener("keydown", handle_key, false); } }) """ % str(rx.Var.create(self.event_triggers["on_key_down"])) ] def global_key_demo(): return rx.vstack( GlobalHotkeyWatcher.create( keys=["a", "s", "d", "w"], on_key_down=lambda key: rx.cond( rx.Var.create(["a", "s", "d", "w"]).contains(key), GlobalHotkeyState.update_key(key), rx.console_log(key), ), ), rx.text("Press a, s, d or w to trigger an event"), rx.heading(f"Last watched key pressed: {GlobalHotkeyState.key}", as_="h2"), ) ``` This snippet can also be imported through pip: [reflex-global-hotkey](https://pypi.org/project/reflex-global-hotkey/). # Browser Storage Source: http://localhost:3000/docs/api-reference/browser-storage.md ## rx.Cookie Represents a state Var that is stored as a cookie in the browser. Currently only supports string values. Parameters - `name` : The name of the cookie on the client side. - `path`: The cookie path. Use `/` to make the cookie accessible on all pages. - `max_age` : Relative max age of the cookie in seconds from when the client receives it. - `domain`: Domain for the cookie (e.g., `sub.domain.com` or `.allsubdomains.com`). - `secure`: If the cookie is only accessible through HTTPS. - `same_site`: Whether the cookie is sent with third-party requests. Can be one of (`True`, `False`, `None`, `lax`, `strict`). ```python class CookieState(rx.State): c1: str = rx.Cookie() c2: str = rx.Cookie("c2 default") # cookies with custom settings c3: str = rx.Cookie(max_age=2) # expires after 2 second c4: str = rx.Cookie(same_site="strict") c5: str = rx.Cookie(path="/foo/") # only accessible on `/foo/` 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!** The Cookie value is only set when the Var is assigned. If you need to set a default value, you can assign a value to the cookie in an `on_load` event handler. ``` ## Accessing Cookies Cookies are accessed like any other Var in the state. If another state needs access to the value of a cookie, the state should be a substate of the state that defines the cookie. Alternatively the `get_state` API can be used to access the other state. For rendering cookies in the frontend, import the state that defines the cookie and reference it directly. ```md alert warning # **Two separate states should _avoid_ defining `rx.Cookie` with the same name.** Although it is technically possible, the cookie options may differ, leading to unexpected results. Additionally, updating the cookie value in one state will not automatically update the value in the other state without a page refresh or navigation event. ``` ## rx.remove_cookie Remove a cookie from the client's browser. Parameters: - `key`: The name of cookie to remove. ```python rx.button("Remove cookie", on_click=rx.remove_cookie("key")) ``` This event can also be returned from an event handler: ```python class CookieState(rx.State): ... def logout(self): return rx.remove_cookie("auth_token") ``` ## rx.LocalStorage Represents a state Var that is stored in localStorage in the browser. Currently only supports string values. Parameters - `name`: The name of the storage key on the client side. - `sync`: Boolean indicates if the state should be kept in sync across tabs of the same browser. ```python class LocalStorageState(rx.State): # local storage with default settings l1: str = rx.LocalStorage() # local storage with custom settings l2: str = rx.LocalStorage("l2 default") l3: str = rx.LocalStorage(name="l3") # local storage that automatically updates in other states across tabs l4: str = rx.LocalStorage(sync=True) ``` ### Syncing Vars Because LocalStorage applies to the entire browser, all LocalStorage Vars are automatically shared across tabs. The `sync` parameter controls whether an update in one tab should be actively propagated to other tabs without requiring a navigation or page refresh event. ## rx.remove_local_storage Remove a local storage item from the client's browser. Parameters - `key`: The key to remove from local storage. ```python rx.button( "Remove Local Storage", on_click=rx.remove_local_storage("key"), ) ``` This event can also be returned from an event handler: ```python class LocalStorageState(rx.State): ... def logout(self): return rx.remove_local_storage("local_storage_state.l1") ``` ## rx.clear_local_storage() Clear all local storage items from the client's browser. This may affect other apps running in the same domain or libraries within your app that use local storage. ```python rx.button( "Clear all Local Storage", on_click=rx.clear_local_storage(), ) ``` ## rx.SessionStorage Represents a state Var that is stored in sessionStorage in the browser. Similar to localStorage, but the data is cleared when the page session ends (when the browser/tab is closed). Currently only supports string values. Parameters - `name`: The name of the storage key on the client side. ```python class SessionStorageState(rx.State): # session storage with default settings s1: str = rx.SessionStorage() # session storage with custom settings s2: str = rx.SessionStorage("s2 default") s3: str = rx.SessionStorage(name="s3") ``` ### Session Persistence SessionStorage data is cleared when the page session ends. A page session lasts as long as the browser is open and survives page refreshes and restores, but is cleared when the tab or browser is closed. Unlike LocalStorage, SessionStorage is isolated to the tab/window in which it was created, so it's not shared with other tabs/windows of the same origin. ## rx.remove_session_storage Remove a session storage item from the client's browser. Parameters - `key`: The key to remove from session storage. ```python rx.button( "Remove Session Storage", on_click=rx.remove_session_storage("key"), ) ``` This event can also be returned from an event handler: ```python class SessionStorageState(rx.State): ... def logout(self): return rx.remove_session_storage("session_storage_state.s1") ``` ## rx.clear_session_storage() Clear all session storage items from the client's browser. This may affect other apps running in the same domain or libraries within your app that use session storage. ```python rx.button( "Clear all Session Storage", on_click=rx.clear_session_storage(), ) ``` ## Serialization Strategies If a non-trivial data structure should be stored in a `Cookie`, `LocalStorage`, or `SessionStorage` var it needs to be serialized before and after storing it. It is recommended to use a pydantic class for the data which provides simple serialization helpers and works recursively in complex object structures. ```python demo exec import reflex as rx import pydantic class AppSettings(pydantic.BaseModel): theme: str = "light" sidebar_visible: bool = True update_frequency: int = 60 error_messages: list[str] = pydantic.Field(default_factory=list) class ComplexLocalStorageState(rx.State): data_raw: str = rx.LocalStorage("{}") data: AppSettings = AppSettings() settings_open: bool = False @rx.event def save_settings(self): self.data_raw = self.data.model_dump_json() self.settings_open = False @rx.event def open_settings(self): self.data = AppSettings.model_validate_json(self.data_raw) self.settings_open = True @rx.event def set_field(self, field, value): setattr(self.data, field, value) def app_settings(): return rx.form.root( rx.foreach( ComplexLocalStorageState.data.error_messages, rx.text, ), rx.form.field( rx.flex( rx.form.label( "Theme", rx.input( value=ComplexLocalStorageState.data.theme, on_change=lambda v: ComplexLocalStorageState.set_field( "theme", v ), ), ), rx.form.label( "Sidebar Visible", rx.switch( checked=ComplexLocalStorageState.data.sidebar_visible, on_change=lambda v: ComplexLocalStorageState.set_field( "sidebar_visible", v, ), ), ), rx.form.label( "Update Frequency (seconds)", rx.input( value=ComplexLocalStorageState.data.update_frequency, on_change=lambda v: ComplexLocalStorageState.set_field( "update_frequency", v, ), ), ), rx.dialog.close(rx.button("Save", type="submit")), gap=2, direction="column", ) ), on_submit=lambda _: ComplexLocalStorageState.save_settings(), ) def app_settings_example(): return rx.dialog.root( rx.dialog.trigger( rx.button("App Settings", on_click=ComplexLocalStorageState.open_settings), ), rx.dialog.content( rx.dialog.title("App Settings"), app_settings(), ), ) ``` ## Comparison of Storage Types Here's a comparison of the different client-side storage options in Reflex: | Feature | rx.Cookie | rx.LocalStorage | rx.SessionStorage | | ------------------- | --------------------------------- | --------------------------- | --------------------------- | | Persistence | Until cookie expires | Until explicitly deleted | Until browser/tab is closed | | Storage Limit | ~4KB | ~5MB | ~5MB | | Sent with Requests | Yes | No | No | | Accessibility | Server & Client | Client Only | Client Only | | Expiration | Configurable | Never | End of session | | Scope | Configurable (domain, path) | Origin (domain) | Tab/Window | | Syncing Across Tabs | No | Yes (with sync=True) | No | | Use Case | Authentication, Server-side state | User preferences, App state | Temporary session data | ## When to Use Each Storage Type ## Use rx.Cookie When: - You need the data to be accessible on the server side (cookies are sent with HTTP requests) - You're handling user authentication - You need fine-grained control over expiration and scope - You need to limit the data to specific paths in your app ## Use rx.LocalStorage When: - You need to store larger amounts of data (up to ~5MB) - You want the data to persist indefinitely (until explicitly deleted) - You need to share data between different tabs/windows of your app - You want to store user preferences that should be remembered across browser sessions ## Use rx.SessionStorage When: - You need temporary data that should be cleared when the browser/tab is closed - You want to isolate data to a specific tab/window - You're storing sensitive information that shouldn't persist after the session ends - You're implementing per-session features like form data, shopping carts, or multi-step processes - You want to persist data for a state after Redis expiration (for server-side state that needs to survive longer than Redis TTL) # CLI Source: http://localhost:3000/docs/api-reference/cli.md The `reflex` command line interface (CLI) is a tool for creating and managing Reflex apps. To see a list of all available commands, run `reflex --help`. ```bash $ reflex --help Usage: reflex [OPTIONS] COMMAND [ARGS]... Reflex CLI to create, run, and deploy apps. Options: --version Show the version and exit. --help Show this message and exit. Commands: cloud The Hosting CLI. component CLI for creating custom components. db Subcommands for managing the database schema. deploy Deploy the app to the Reflex hosting service. export Export the app to a zip file. init Initialize a new Reflex app in the current directory. login Authenticate with experimental Reflex hosting service. logout Log out of access to Reflex hosting service. rename Rename the app in the current directory. run Run the app in the current directory. script Subcommands for running helper scripts. ``` ## Init The `reflex init` command creates a new Reflex app in the current directory. If an `rxconfig.py` file already exists already, it will re-initialize the app with the latest template. ```bash $ reflex init --help Usage: reflex init [OPTIONS] Initialize a new Reflex app in the current directory. Options: --name APP_NAME The name of the app to initialize. --template [demo|sidebar|blank] The template to initialize the app with. --loglevel [debug|info|warning|error|critical] The log level to use. [default: LogLevel.INFO] --help Show this message and exit. ``` ## Run The `reflex run` command runs the app in the current directory. By default it runs your app in development mode. This means that the app will automatically reload when you make changes to the code. You can also run in production mode which will create an optimized build of your app. You can configure the mode, as well as other options through flags. ```bash $ reflex run --help Usage: reflex run [OPTIONS] Run the app in the current directory. Options: --env [dev|prod] The environment to run the app in. [default: Env.DEV] --frontend-only Execute only frontend. --backend-only Execute only backend. --frontend-port TEXT Specify a different frontend port. [default: 3000] --backend-port TEXT Specify a different backend port. [default: 8000] --backend-host TEXT Specify the backend host. [default: 0.0.0.0] --loglevel [debug|info|warning|error|critical] The log level to use. [default: LogLevel.INFO] --help Show this message and exit. ``` ## Export You can export your app's frontend and backend to zip files using the `reflex export` command. The frontend is a compiled NextJS app, which can be deployed to a static hosting service like Github Pages or Vercel. However this is just a static build, so you will need to deploy the backend separately. See the self-hosting guide for more information. ## Rename The `reflex rename` command allows you to rename your Reflex app. This updates the app name in the configuration files. ```bash $ reflex rename --help Usage: reflex rename [OPTIONS] NEW_NAME Rename the app in the current directory. Options: --loglevel [debug|default|info|warning|error|critical] The log level to use. --help Show this message and exit. ``` ## Cloud The `reflex cloud` command provides access to the Reflex Cloud hosting service. It includes subcommands for managing apps, projects, secrets, and more. For detailed documentation on Reflex Cloud and deployment, see the [Cloud Quick Start Guide](https://reflex.dev/docs/hosting/deploy-quick-start/). ## Script The `reflex script` command provides access to helper scripts for Reflex development. ```bash $ reflex script --help Usage: reflex script [OPTIONS] COMMAND [ARGS]... Subcommands for running helper scripts. Options: --help Show this message and exit. ``` # Event Triggers Source: http://localhost:3000/docs/api-reference/event-triggers.md ```python exec from datetime import datetime import reflex as rx ``` Components can modify the state based on user events such as clicking a button or entering text in a field. These events are triggered by event triggers. Event triggers are component specific and are listed in the documentation for each component. ## Component Lifecycle Events Reflex components have lifecycle events like `on_mount` and `on_unmount` that allow you to execute code at specific points in a component's existence. These events are crucial for initializing data, cleaning up resources, and creating dynamic user interfaces. ### When Lifecycle Events Are Activated - **on_mount**: This event is triggered immediately after a component is rendered and attached to the DOM. It fires: - When a page containing the component is first loaded - When a component is conditionally rendered (appears after being hidden) - When navigating to a page containing the component using internal navigation - It does NOT fire when the page is refreshed or when following external links - **on_unmount**: This event is triggered just before a component is removed from the DOM. It fires: - When navigating away from a page containing the component using internal navigation - When a component is conditionally removed from the DOM (e.g., via a condition that hides it) - It does NOT fire when refreshing the page, closing the browser tab, or following external links ## Page Load Events In addition to component lifecycle events, Reflex also provides page-level events like `on_load` that are triggered when a page loads. The `on_load` event is useful for: - Fetching data when a page first loads - Checking authentication status - Initializing page-specific state - Setting default values for cookies or browser storage You can specify an event handler to run when the page loads using the `on_load` parameter in the `@rx.page` decorator or `app.add_page()` method: ```python class State(rx.State): data: dict = dict() @rx.event def get_data(self): # Fetch data when the page loads self.data = fetch_data() @rx.page(on_load=State.get_data) def index(): return rx.text("Data loaded on page load") ``` This is particularly useful for authentication checks: ```python class State(rx.State): authenticated: bool = False @rx.event def check_auth(self): # Check if user is authenticated self.authenticated = check_auth() if not self.authenticated: return rx.redirect("/login") @rx.page(on_load=State.check_auth) def protected_page(): return rx.text("Protected content") ``` For more details on page load events, see the [page load events documentation](/docs/events/page-load-events). ## Event Reference --- ## on_focus The on_focus event handler is called when the element (or some element inside of it) receives focus. For example, it's called when the user clicks on a text input. ```python demo exec class FocusState(rx.State): text: str = "Change Me!" @rx.event def change_text(self, text): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def focus_example(): return rx.input(value=FocusState.text, on_focus=FocusState.change_text) ``` ## on_blur The on_blur event handler is called when focus has left the element (or left some element inside of it). For example, it's called when the user clicks outside of a focused text input. ```python demo exec class BlurState(rx.State): text: str = "Change Me!" @rx.event def change_text(self, text): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def blur_example(): return rx.input(value=BlurState.text, on_blur=BlurState.change_text) ``` ## on_change The on_change event handler is called when the value of an element has changed. For example, it's called when the user types into a text input each keystroke triggers the on change. ```python demo exec class ChangeState(rx.State): checked: bool = False @rx.event def set_checked(self): self.checked = not self.checked def change_example(): return rx.switch(on_change=ChangeState.set_checked) ``` ## on_click The on_click event handler is called when the user clicks on an element. For example, it's called when the user clicks on a button. ```python demo exec class ClickState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def click_example(): return rx.button(ClickState.text, on_click=ClickState.change_text) ``` ## on_context_menu The on_context_menu event handler is called when the user right-clicks on an element. For example, it's called when the user right-clicks on a button. ```python demo exec class ContextState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def context_menu_example(): return rx.button(ContextState.text, on_context_menu=ContextState.change_text) ``` ## on_double_click The on_double_click event handler is called when the user double-clicks on an element. For example, it's called when the user double-clicks on a button. ```python demo exec class DoubleClickState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def double_click_example(): return rx.button( DoubleClickState.text, on_double_click=DoubleClickState.change_text ) ``` ## on_mount The on_mount event handler is called after the component is rendered on the page. It is similar to a page on_load event, although it does not necessarily fire when navigating between pages. This event is particularly useful for initializing data, making API calls, or setting up component-specific state when a component first appears. ```python demo exec class MountState(rx.State): events: list[str] = [] data: list[dict] = [] loading: bool = False @rx.event def on_mount(self): self.events = self.events[-4:] + ["on_mount @ " + str(datetime.now())] @rx.event async def load_data(self): self.loading = True yield import asyncio await asyncio.sleep(1) self.data = [dict(id=1, name="Item 1"), dict(id=2, name="Item 2")] self.loading = False def mount_example(): return rx.vstack( rx.heading("Component Lifecycle Demo", as_="h2"), rx.foreach(MountState.events, rx.text), rx.cond( MountState.loading, rx.spinner(), rx.foreach( MountState.data, lambda item: rx.text(f"ID: {item['id']} - {item['name']}"), ), ), on_mount=MountState.on_mount, ) ``` ## on_unmount The on_unmount event handler is called after removing the component from the page. However, on_unmount will only be called for internal navigation, not when following external links or refreshing the page. This event is useful for cleaning up resources, saving state, or performing cleanup operations before a component is removed from the DOM. ```python demo exec class UnmountState(rx.State): events: list[str] = [] resource_id: str = "resource-12345" status: str = "Resource active" @rx.event def on_unmount(self): self.events = self.events[-4:] + ["on_unmount @ " + str(datetime.now())] self.status = f"Resource {self.resource_id} cleaned up" @rx.event def initialize_resource(self): self.status = f"Resource {self.resource_id} initialized" def unmount_example(): return rx.vstack( rx.heading("Unmount Demo", as_="h2"), rx.foreach(UnmountState.events, rx.text), rx.text(UnmountState.status), rx.link( rx.button("Navigate Away (Triggers Unmount)"), href="/", ), on_mount=UnmountState.initialize_resource, on_unmount=UnmountState.on_unmount, ) ``` ## on_mouse_up The on_mouse_up event handler is called when the user releases a mouse button on an element. For example, it's called when the user releases the left mouse button on a button. ```python demo exec class MouseUpState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_up_example(): return rx.button(MouseUpState.text, on_mouse_up=MouseUpState.change_text) ``` ## on_mouse_down The on_mouse_down event handler is called when the user presses a mouse button on an element. For example, it's called when the user presses the left mouse button on a button. ```python demo exec class MouseDownState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_down_example(): return rx.button(MouseDownState.text, on_mouse_down=MouseDownState.change_text) ``` ## on_mouse_enter The on_mouse_enter event handler is called when the user's mouse enters an element. For example, it's called when the user's mouse enters a button. ```python demo exec class MouseEnterState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_enter_example(): return rx.button(MouseEnterState.text, on_mouse_enter=MouseEnterState.change_text) ``` ## on_mouse_leave The on_mouse_leave event handler is called when the user's mouse leaves an element. For example, it's called when the user's mouse leaves a button. ```python demo exec class MouseLeaveState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_leave_example(): return rx.button(MouseLeaveState.text, on_mouse_leave=MouseLeaveState.change_text) ``` ## on_mouse_move The on_mouse_move event handler is called when the user moves the mouse over an element. For example, it's called when the user moves the mouse over a button. ```python demo exec class MouseMoveState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_move_example(): return rx.button(MouseMoveState.text, on_mouse_move=MouseMoveState.change_text) ``` ## on_mouse_out The on_mouse_out event handler is called when the user's mouse leaves an element. For example, it's called when the user's mouse leaves a button. ```python demo exec class MouseOutState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_out_example(): return rx.button(MouseOutState.text, on_mouse_out=MouseOutState.change_text) ``` ## on_mouse_over The on_mouse_over event handler is called when the user's mouse enters an element. For example, it's called when the user's mouse enters a button. ```python demo exec class MouseOverState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def mouse_over_example(): return rx.button(MouseOverState.text, on_mouse_over=MouseOverState.change_text) ``` ## on_scroll The on_scroll event handler is called when the user scrolls the page. For example, it's called when the user scrolls the page down. ```python demo exec class ScrollState(rx.State): text: str = "Change Me!" @rx.event def change_text(self): if self.text == "Change Me!": self.text = "Changed!" else: self.text = "Change Me!" def scroll_example(): return rx.vstack( rx.text("Scroll to make the text below change."), rx.text(ScrollState.text), rx.text("Scroll to make the text above change."), on_scroll=ScrollState.change_text, overflow="auto", height="3em", 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, ), ) ``` # Plugins Source: http://localhost:3000/docs/api-reference/plugins.md ```python exec import reflex as rx ``` Reflex supports a plugin system that allows you to extend the framework's functionality during the compilation process. Plugins can add frontend dependencies, modify build configurations, generate static assets, and perform custom tasks before compilation. ## Configuring Plugins Plugins are configured in your `rxconfig.py` file using the `plugins` parameter: ```python import reflex as rx config = rx.Config( app_name="my_app", plugins=[ rx.plugins.SitemapPlugin(), rx.plugins.TailwindV4Plugin(), ], ) ``` ## Built-in Plugins Reflex comes with several built-in plugins that provide common functionality. ### SitemapPlugin The `SitemapPlugin` automatically generates a sitemap.xml file for your application, which helps search engines discover and index your pages. ```python import reflex as rx config = rx.Config( app_name="my_app", plugins=[ rx.plugins.SitemapPlugin(), ], ) ``` The sitemap plugin automatically includes all your app's routes. For dynamic routes or custom configuration, you can add sitemap metadata to individual pages: ```python @rx.page( route="/blog/[slug]", context={"sitemap": {"changefreq": "weekly", "priority": 0.8}} ) def blog_post(): return rx.text("Blog post content") @rx.page( route="/about", context={"sitemap": {"changefreq": "monthly", "priority": 0.5}} ) def about(): return rx.text("About page") ``` The sitemap configuration supports the following options: - `loc`: Custom URL for the page (required for dynamic routes) - `lastmod`: Last modification date (datetime object) - `changefreq`: How frequently the page changes (`"always"`, `"hourly"`, `"daily"`, `"weekly"`, `"monthly"`, `"yearly"`, `"never"`) - `priority`: Priority of this URL relative to other URLs (0.0 to 1.0) ### TailwindV4Plugin The `TailwindV4Plugin` provides support for Tailwind CSS v4, which is the recommended version for new projects and includes performance improvements and new features. ```python import reflex as rx # Basic configuration config = rx.Config( app_name="my_app", plugins=[ rx.plugins.TailwindV4Plugin(), ], ) ``` You can customize the Tailwind configuration by passing a config dictionary: ```python import reflex as rx tailwind_config = { "theme": { "extend": { "colors": { "brand": { "50": "#eff6ff", "500": "#3b82f6", "900": "#1e3a8a", } } } }, "plugins": ["@tailwindcss/typography"], } config = rx.Config( app_name="my_app", plugins=[ rx.plugins.TailwindV4Plugin(tailwind_config), ], ) ``` ### TailwindV3Plugin The `TailwindV3Plugin` integrates Tailwind CSS v3 into your Reflex application. While still supported, TailwindV4Plugin is recommended for new projects. ```python import reflex as rx # Basic configuration config = rx.Config( app_name="my_app", plugins=[ rx.plugins.TailwindV3Plugin(), ], ) ``` You can customize the Tailwind configuration by passing a config dictionary: ```python import reflex as rx tailwind_config = { "theme": { "extend": { "colors": { "primary": "#3b82f6", "secondary": "#64748b", } } }, "plugins": ["@tailwindcss/typography", "@tailwindcss/forms"], } config = rx.Config( app_name="my_app", plugins=[ rx.plugins.TailwindV3Plugin(tailwind_config), ], ) ``` ## Plugin Management ### Default Plugins Some plugins are enabled by default. Currently, the `SitemapPlugin` is enabled automatically. If you want to disable a default plugin, use the `disable_plugins` parameter: ```python import reflex as rx config = rx.Config( app_name="my_app", disable_plugins=["reflex.plugins.sitemap.SitemapPlugin"], ) ``` ### Plugin Order Plugins are executed in the order they appear in the `plugins` list. This can be important if plugins have dependencies on each other or modify the same files. ```python import reflex as rx config = rx.Config( app_name="my_app", plugins=[ rx.plugins.TailwindV4Plugin(), # Runs first rx.plugins.SitemapPlugin(), # Runs second ], ) ``` ## Plugin Architecture All plugins inherit from the base `Plugin` class and can implement several lifecycle methods: ```python class Plugin: def get_frontend_development_dependencies(self, **context) -> list[str]: """Get NPM packages required by the plugin for development.""" return [] def get_frontend_dependencies(self, **context) -> list[str]: """Get NPM packages required by the plugin.""" return [] def get_static_assets(self, **context) -> Sequence[tuple[Path, str | bytes]]: """Get static assets required by the plugin.""" return [] def get_stylesheet_paths(self, **context) -> Sequence[str]: """Get paths to stylesheets required by the plugin.""" return [] def register_route(self, **context) -> None: """Contribute pages before the app's first compilation.""" pass def pre_compile(self, **context) -> None: """Called before compilation to perform custom tasks.""" pass ``` ### Creating Custom Plugins You can create custom plugins by inheriting from the base `Plugin` class: ```python from reflex.plugins.base import Plugin from pathlib import Path class CustomPlugin(Plugin): def get_frontend_dependencies(self, **context): return ["my-custom-package@1.0.0"] def pre_compile(self, **context): # Custom logic before compilation print("Running custom plugin logic...") # Add a custom task context["add_save_task"](self.create_custom_file) def create_custom_file(self): return "public/custom.txt", "Custom content" ``` Then use it in your configuration: ```python import reflex as rx from my_plugins import CustomPlugin config = rx.Config( app_name="my_app", plugins=[ CustomPlugin(), ], ) ``` ### Contributing Pages A plugin can contribute pages by overriding `register_route`. The hook runs once for each `App`, after app-defined pages have been collected and before any page is evaluated. A fresh app created by development reload or a new process runs the hooks again. Use the provided `add_page` capability rather than calling `app.add_page` directly. The framework collects every plugin's calls first and commits them only after all route hooks succeed, so a failing hook cannot leave partial pages or dynamic route arguments on the app: ```python class DashboardPlugin(Plugin): def __init__(self, route="/dashboard"): self.route = route def register_route(self, *, add_page, has_app_page, **context): # Let an app-defined page override this optional default. if not has_app_page(self.route): add_page(dashboard_page, route=self.route, title="Dashboard") ``` `add_page` accepts the same page options as `App.add_page` (`title`, `on_load`, `meta`, ...) but takes them as keyword arguments only; its signature is described by the `AddPageProtocol` in the hook context. It uses the app's normal page-preparation path while collecting the descriptor, then the framework commits the prepared descriptors together. Adding a route already defined by the app or by another plugin raises a `RouteValueError` identifying the owner. `has_app_page(route)` checks only app-defined pages; it intentionally ignores pages staged by earlier plugins so plugin conflicts do not silently depend on configuration order. The hook also receives `app_type`, which supports concrete-app compatibility checks without exposing the mutable `App` instance. An `App` subclass that adds page-registration keyword arguments must normalize them in its protected `_prepare_page` override and return the base prepared-page descriptor. That method must not mutate app route state: plugin hooks call it during collection, while the framework owns the final batch commit. The public `add_page` and protected `_commit_page` overrides remain the direct-registration API but are intentionally not dispatched during a staged plugin batch, so an override cannot make the batch commit partially. ### Looking Up Configured Plugins Use `rx.plugins.get_plugin(PluginType)` when runtime behavior needs the plugin instance declared in `rxconfig.py`. It returns the single configured instance matching that type (including subclasses), returns `None` when none matches, and raises `ConfigError` when the lookup is ambiguous. Runtime-readable values should be derived from the plugin's constructor arguments so every process can recreate the same behavior from `rxconfig.plugins` without a separate binding step. # Special Events Source: http://localhost:3000/docs/api-reference/special-events.md ```python exec import reflex as rx ``` Reflex includes a set of built-in special events that can be utilized as event triggers or returned from event handlers in your applications. These events enhance interactivity and user experience. Below are the special events available in Reflex, along with explanations of their functionality: ## rx.console_log Perform a console.log in the browser's console. ```python demo rx.button("Log", on_click=rx.console_log("Hello World!")) ``` When triggered, this event logs a specified message to the browser's developer console. It's useful for debugging and monitoring the behavior of your application. ## rx.scroll_to scroll to an element in the page ```python demo 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. ### Parameters - `path`: The destination path or URL to which the user should be redirected. - `external`: If set to True, the redirection will open in a new tab. Defaults to `False`. ```python demo rx.vstack( rx.button( "open in tab", on_click=rx.redirect("/docs/api-reference/special-events") ), rx.button( "open in new tab", on_click=rx.redirect("https://github.com/reflex-dev/reflex/", is_external=True), ), ) ``` When this event is triggered, it navigates the user to a different page or location within your Reflex application. By default, the redirection occurs in the same tab. However, if you set the external parameter to True, the redirection will open in a new tab or window, providing a seamless user experience. This event can also be run from an event handler in State. It is necessary to `return` the `rx.redirect()`. ```python demo exec class RedirectExampleState(rx.State): """The app state.""" @rx.event def change_page(self): return rx.redirect("https://github.com/reflex-dev/reflex/", is_external=True) def redirect_example(): return rx.vstack( rx.button("Change page in State", on_click=RedirectExampleState.change_page), ) ``` ## rx.set_clipboard Set the specified text content to the clipboard. ```python demo rx.button( 'Copy "Hello World" to clipboard', on_click=rx.set_clipboard("Hello World"), ) ``` 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. ```python demo rx.hstack( rx.input(id="input1"), rx.button("Erase", on_click=rx.set_value("input1", "")), ) ``` With this event, you can modify the value of a particular HTML element, typically an input field or another form element. ## rx.window_alert Create a window alert in the browser. ```python demo rx.button("Alert", on_click=rx.window_alert("Hello World!")) ``` ## rx.download Download a file at a given path. Parameters: - `url`: The URL of the file to be downloaded. - `data`: The data to be downloaded. Should be `str` or `bytes`, `data:` URI, `PIL.Image`, or any state Var (to be converted to JSON). - `filename`: The desired filename of the downloaded file. ```md alert # `url` and `data` args are mutually exclusive, and at least one of them must be provided. ``` ```python demo rx.button( "Download", on_click=rx.download( url="/reflex_banner.webp", filename="different_name_logo.webp" ), 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, ) ``` # Telemetry Source: http://localhost:3000/docs/api-reference/telemetry.md Reflex collects anonymized usage statistics — counts of components and built-in features used at compile time, along with versions of Reflex, Python, and the host OS — so we can prioritize bug fixes and guide the future direction of the framework. We do **not** collect the contents of your source files, state, or any data your app handles at runtime. For full details, see our [Privacy Policy](https://build.reflex.dev/privacy-policy) and [Terms of Service](https://build.reflex.dev/terms-of-use). If you log in to [Reflex Cloud](/docs/hosting/deploy-quick-start/), your Cloud account identifier is associated with subsequent usage events from that machine. ## Opt out In your `rxconfig.py`: ```python import reflex as rx config = rx.Config( app_name="my_app", telemetry_enabled=False, ) ``` Or via environment variable: ```bash REFLEX_TELEMETRY_ENABLED=false reflex init --template blank ``` The environment variable is the only supported opt-out method for reflex commands executed outside the context of an app directory, such as `reflex login` and `reflex init`. # Utility Functions Source: http://localhost:3000/docs/api-reference/utils.md ```python exec import reflex as rx ``` Reflex provides utility functions to help with common tasks in your applications. ## run_in_thread The `run_in_thread` function allows you to run a **non-async** function in a separate thread, which is useful for preventing long-running operations from blocking the UI event queue. ```python async def run_in_thread(func: Callable) -> Any ``` ### Parameters - `func`: The non-async function to run in a separate thread. ### Returns - The return value of the function. ### Raises - `ValueError`: If the function is an async function. ### Usage ```python demo exec id=run_in_thread_demo import asyncio import dataclasses import time import reflex as rx def quick_blocking_function(): time.sleep(0.5) return "Quick task completed successfully!" def slow_blocking_function(): time.sleep(3.0) return "This should never be returned due to timeout!" @dataclasses.dataclass class TaskInfo: result: str = "No result yet" status: str = "Idle" class RunInThreadState(rx.State): tasks: list[TaskInfo] = [] @rx.event(background=True) async def run_quick_task(self): """Run a quick task that completes within the timeout.""" async with self: task_ix = len(self.tasks) self.tasks.append(TaskInfo(status="Running quick task...")) task_info = self.tasks[task_ix] try: result = await rx.run_in_thread(quick_blocking_function) async with self: task_info.result = result task_info.status = "Complete" except Exception as e: async with self: task_info.result = f"Error: {str(e)}" task_info.status = "Failed" @rx.event(background=True) async def run_slow_task(self): """Run a slow task that exceeds the timeout.""" async with self: task_ix = len(self.tasks) self.tasks.append(TaskInfo(status="Running slow task...")) task_info = self.tasks[task_ix] try: # Run with a timeout of 1 second (not enough time) result = await asyncio.wait_for( rx.run_in_thread(slow_blocking_function), timeout=1.0, ) async with self: task_info.result = result task_info.status = "Complete" except asyncio.TimeoutError: async with self: # Warning: even though we stopped waiting for the task, # it may still be running in thread task_info.result = "Task timed out after 1 second!" task_info.status = "Timeout" except Exception as e: async with self: task_info.result = f"Error: {str(e)}" task_info.status = "Failed" def run_in_thread_example(): return rx.vstack( rx.heading("run_in_thread Example", as_="h2", size="3"), rx.hstack( rx.button( "Run Quick Task", on_click=RunInThreadState.run_quick_task, color_scheme="green", ), rx.button( "Run Slow Task (exceeds timeout)", on_click=RunInThreadState.run_slow_task, color_scheme="red", ), ), rx.vstack( rx.foreach( RunInThreadState.tasks.reverse()[:10], lambda task: rx.hstack( rx.text(task.status), rx.spacer(), rx.text(task.result), ), ), align="start", width="100%", ), width="100%", align_items="start", spacing="4", ) ``` ### When to Use run_in_thread Use `run_in_thread` when you need to: 1. Execute CPU-bound operations that would otherwise block the event loop 2. Call synchronous libraries that don't have async equivalents 3. Prevent long-running operations from blocking UI responsiveness ### Example: Processing a Large File ```python import reflex as rx import time class FileProcessingState(rx.State): progress: str = "Ready" @rx.event(background=True) async def process_large_file(self): async with self: self.progress = "Processing file..." def process_file(): # Simulate processing a large file time.sleep(5) return "File processed successfully!" # Save the result to a local variable to avoid blocking the event loop. result = await rx.run_in_thread(process_file) async with self: # Then assign the local result to the state while holding the lock. self.progress = result ``` # Reflex's Var System Source: http://localhost:3000/docs/api-reference/var-system.md ## Motivation Reflex supports some basic operations in state variables on the frontend. Reflex automatically converts variable operations from Python into a JavaScript equivalent. Here's an example of a Reflex conditional in Python that returns "Pass" if the threshold is equal to or greater than 50 and "Fail" otherwise: ```py rx.cond( State.threshold >= 50, "Pass", "Fail", ) ``` The conditional to roughly the following in Javascript: ```js state.threshold >= 50 ? "Pass" : "Fail"; ``` ## Overview Simply put, a `Var` in Reflex represents a Javascript expression. If the type is known, it can be any of the following: - `NumberVar` represents an expression that evaluates to a Javascript `number`. `NumberVar` can support both integers and floating point values - `BooleanVar` represents a boolean expression. For example: `false`, `3 > 2`. - `StringVar` represents an expression that evaluates to a string. For example: `'hello'`, `(2).toString()`. - `ArrayVar` represents an expression that evaluates to an array object. For example: `[1, 2, 3]`, `'words'.split()`. - `ObjectVar` represents an expression that evaluates to an object. For example: `{a: 2, b: 3}`, `{deeply: {nested: {value: false}}}`. - `NoneVar` represent null values. These can be either `undefined` or `null`. ## Creating Vars State fields are converted to `Var` by default. Additionally, you can create a `Var` from Python values using `rx.Var.create()`: ```py rx.Var.create(4) # NumberVar rx.Var.create("hello") # StringVar rx.Var.create([1, 2, 3]) # ArrayVar ``` If you want to explicitly create a `Var` from a raw Javascript string, you can instantiate `rx.Var` directly: ```py rx.Var("2", _var_type=int).guess_type() # NumberVar ``` In the example above, `.guess_type()` will attempt to downcast from a generic `Var` type into `NumberVar`. For this example, calling the function `.to(int)` can also be used in place of `.guess_type()`. ## Operations The `Var` system also supports some other basic operations. For example, `NumberVar` supports basic arithmetic operations like `+` and `-`, as in Python. It also supports comparisons that return a `BooleanVar`. Custom `Var` operations can also be defined: ```py from reflex.vars import var_operation, var_operation_return, ArrayVar, NumberVar @var_operation def multiply_array_values(a: ArrayVar): return var_operation_return( js_expression=f"{a}.reduce((p, c) => p * c, 1)", var_type=int, ) def factorial(value: NumberVar): return rx.cond(value <= 1, 1, multiply_array_values(rx.Var.range(1, value + 1))) ``` Use `js_expression` to pass explicit JavaScript expressions; in the `multiply_array_values` example, we pass in a JavaScript expression that calculates the product of all elements in an array called `a` by using the reduce method to multiply each element with the accumulated result, starting from an initial value of 1. Later, we leverage `rx.cond` in the' factorial' function, we instantiate an array using the `range` function, and pass this array to `multiply_array_values`. # API Routes Overview Source: http://localhost:3000/docs/api-routes/overview.md ```python exec import reflex as rx ``` In addition to your frontend app, Reflex uses a FastAPI backend to serve your app. The API transformer feature allows you to transform or extend the ASGI app that serves your Reflex application. ## Overview The API transformer provides a way to: 1. Integrate existing FastAPI or Starlette applications with your Reflex app 2. Apply middleware or transformations to the ASGI app 3. Extend your Reflex app with additional API endpoints This is useful for creating a backend API that can be used for purposes beyond your Reflex app, or for integrating Reflex with existing backend services. ## Using API Transformer You can set the `api_transformer` parameter when initializing your Reflex app: ```python import reflex as rx from fastapi import FastAPI, Depends from fastapi.security import OAuth2PasswordBearer # Create a FastAPI app fastapi_app = FastAPI(title="My API") # Add routes to the FastAPI app @fastapi_app.get("/api/items") async def get_items(): return dict(items=["Item1", "Item2", "Item3"]) # Create a Reflex app with the FastAPI app as the API transformer app = rx.App(api_transformer=fastapi_app) ``` ## Types of API Transformers The `api_transformer` parameter can accept: 1. A Starlette or FastAPI instance 2. A callable that takes an ASGIApp and returns an ASGIApp 3. A sequence of the above ### Using a FastAPI or Starlette Instance When you provide a FastAPI or Starlette instance as the API transformer, Reflex will mount its internal API to your app, allowing you to define additional routes: ```python import reflex as rx from fastapi import FastAPI, Depends from fastapi.security import OAuth2PasswordBearer # Create a FastAPI app with authentication fastapi_app = FastAPI(title="Secure API") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # Add a protected route @fastapi_app.get("/api/protected") async def protected_route(token: str = Depends(oauth2_scheme)): return dict(message="This is a protected endpoint") # Create a token endpoint @fastapi_app.post("/token") async def login(username: str, password: str): # In a real app, you would validate credentials if username == "user" and password == "password": return dict(access_token="example_token", token_type="bearer") return dict(error="Invalid credentials") # Create a Reflex app with the FastAPI app as the API transformer app = rx.App(api_transformer=fastapi_app) ``` ### Using a Callable Transformer You can also provide a callable that transforms the ASGI app: ```python import reflex as rx from starlette.middleware.cors import CORSMiddleware # Create a transformer function that returns a transformed ASGI app def add_cors_middleware(app): # Wrap the app with CORS middleware and return the wrapped app return CORSMiddleware( app=app, allow_origins=["https://example.com"], allow_methods=["*"], allow_headers=["*"], ) # Create a Reflex app with the transformer app = rx.App(api_transformer=add_cors_middleware) ``` ### Using Multiple Transformers You can apply multiple transformers by providing a sequence: ```python import reflex as rx from fastapi import FastAPI from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware # Create a FastAPI app fastapi_app = FastAPI(title="My API") # Add routes to the FastAPI app @fastapi_app.get("/api/items") async def get_items(): return dict(items=["Item1", "Item2", "Item3"]) # Create a transformer function def add_logging_middleware(app): # This is a simple example middleware that logs requests async def middleware(scope, receive, send): # Log the request path path = scope["path"] print("Request:", path) await app(scope, receive, send) return middleware # Create a Reflex app with multiple transformers app = rx.App(api_transformer=[fastapi_app, add_logging_middleware]) ``` ## Reserved Routes Some routes on the backend are reserved for the runtime of Reflex, and should not be overridden unless you know what you are doing. ### Ping `localhost:8000/ping/`: You can use this route to check the health of the backend. The expected return is `"pong"`. ### Event `localhost:8000/_event`: the frontend will use this route to notify the backend that an event occurred. ```md alert error # Overriding this route will break the event communication ``` ### Upload `localhost:8000/_upload`: This route is used for file uploads when using `rx.upload()`, including `rx.upload_files(...)` and `rx.upload_files_chunk(...)`. # Assets Source: http://localhost:3000/docs/assets/overview.md ```python exec import reflex as rx ``` Static files such as images and stylesheets can be placed in `assets/` folder of the project. These files can be referenced within your app. ```md alert # Assets are copied during the build process. Any files placed within the `assets/` folder at runtime will not be available to the app when running in production mode. The `assets/` folder should only be used for static files. ``` ## Referencing Assets There are two ways to reference assets in your Reflex app: ### 1. Direct Path Reference To reference an image in the `assets/` folder, pass the relative path as a prop. For example, you can store your logo in your assets folder: ```bash assets └── Reflex.svg ``` Then you can display it using a `rx.image` component: ```python demo rx.image( src="https://web.reflex-assets.dev/other/Reflex.svg", alt="Reflex logo", width="5em" ) ``` ```md alert # Always prefix the asset path with a forward slash `/` to reference the asset from the root of the project, or it may not display correctly on non-root pages. ``` ### 2. Using rx.asset Function The `rx.asset` function provides a more flexible way to reference assets in your app. It supports both local assets (in the app's `assets/` directory) and shared assets (placed next to your Python files). #### Local Assets Local assets are stored in the app's `assets/` directory and are referenced using `rx.asset`: ```python demo rx.image(src=rx.asset("Reflex.svg"), alt="Reflex logo", width="5em") ``` #### Shared Assets Shared assets are placed next to your Python file and are linked to the app's external assets directory. This is useful for creating reusable components with their own assets: ```python box # my_component.py import reflex as rx # my_script.js is located in the same directory as this Python file def my_component(): return rx.box( rx.script(src=rx.asset("my_script.js", shared=True)), "Component with custom script", ) ``` You can also specify a subfolder for shared assets: ```python box # my_component.py import reflex as rx # image.png is located in a subfolder next to this Python file def my_component_with_image(): return rx.image( src=rx.asset("image.png", shared=True, subfolder="images"), alt="Example image asset", ) ``` ```md alert # Shared assets are linked to your app via symlinks. When using `shared=True`, the asset is symlinked from its original location to your app's external assets directory. This allows you to keep assets alongside their related code. ``` ## Favicon The favicon is the small icon that appears in the browser tab. You can add a `favicon.ico` file to the `assets/` folder to change the favicon. # Files Source: http://localhost:3000/docs/assets/upload-and-download-files.md ```python exec import reflex as rx ``` In addition to any assets you ship with your app, many web app will often need to receive or send files, whether you want to share media, allow user to import their data, or export some backend data. In this section, we will cover all you need to know for manipulating files in Reflex. ## Assets vs Upload Directory Before diving into file uploads and downloads, it's important to understand the difference between assets and the upload directory in Reflex: ```python eval # Simple table comparing assets vs upload directory rx.table.root( rx.table.header( rx.table.row( rx.table.column_header_cell("Feature"), rx.table.column_header_cell("Assets"), rx.table.column_header_cell("Upload Directory"), ), ), rx.table.body( rx.table.row( rx.table.cell(rx.text("Purpose", font_weight="bold")), rx.table.cell( rx.text( "Static files included with your app (images, stylesheets, scripts)" ) ), rx.table.cell(rx.text("Dynamic files uploaded by users during runtime")), ), rx.table.row( rx.table.cell(rx.text("Location", font_weight="bold")), rx.table.cell( rx.hstack( rx.code( "assets/", style={ "color": rx.color("violet", 11), "border_radius": "0.25rem", "border": f"1px solid {rx.color('violet', 5)}", "background": rx.color("violet", 3), }, ), rx.text(" folder or next to Python files (shared assets)"), spacing="2", ) ), rx.table.cell( rx.hstack( rx.code( "uploaded_files/", style={ "color": rx.color("violet", 11), "border_radius": "0.25rem", "border": f"1px solid {rx.color('violet', 5)}", "background": rx.color("violet", 3), }, ), rx.text(" directory (configurable)"), spacing="2", ) ), ), rx.table.row( rx.table.cell(rx.text("Access Method", font_weight="bold")), rx.table.cell( rx.hstack( rx.code( "rx.asset()", style={ "color": rx.color("violet", 11), "border_radius": "0.25rem", "border": f"1px solid {rx.color('violet', 5)}", "background": rx.color("violet", 3), }, ), rx.text(" or direct path reference"), spacing="2", ) ), rx.table.cell( rx.code( "rx.get_upload_url()", style={ "color": rx.color("violet", 11), "border_radius": "0.25rem", "border": f"1px solid {rx.color('violet', 5)}", "background": rx.color("violet", 3), }, ) ), ), rx.table.row( rx.table.cell(rx.text("When to Use", font_weight="bold")), rx.table.cell( rx.text("For files that are part of your application's codebase") ), rx.table.cell( rx.text( "For files that users upload or generate through your application" ) ), ), rx.table.row( rx.table.cell(rx.text("Availability", font_weight="bold")), rx.table.cell(rx.text("Available at compile time")), rx.table.cell(rx.text("Available at runtime")), ), ), width="100%", ) ``` For more information about assets, see the [Assets Overview](/docs/assets/overview/). ## Download If you want to let the users of your app download files from your server to their computer, Reflex offer you two way. ### With a regular link For some basic usage, simply providing the path to your resource in a `rx.link` will work, and clicking the link will download or display the resource. ```python demo rx.link("Download", href="/reflex_banner.webp") ``` ### With `rx.download` event Using the `rx.download` event will always prompt the browser to download the file, even if it could be displayed in the browser. The `rx.download` event also allows the download to be triggered from another backend event handler. ```python demo rx.button( "Download", on_click=rx.download(url="/reflex_banner.webp"), ) ``` `rx.download` lets you specify a name for the file that will be downloaded, if you want it to be different from the name on the server side. ```python demo rx.button( "Download and Rename", on_click=rx.download(url="/reflex_banner.webp", filename="different_name_logo.png"), ) ``` If the data to download is not already available at a known URL, pass the `data` directly to the `rx.download` event from the backend. ```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 ) ``` The `data` arg accepts `str` or `bytes` data, a `data:` URI, `PIL.Image`, or any state Var. If the Var is not already a string, it will be converted to a string using `JSON.stringify`. This allows complex state structures to be offered as JSON downloads. Reference page for `rx.download` [here](/docs/api-reference/special-events#rx.download). ## Upload Uploading files to your server let your users interact with your app in a different way than just filling forms to provide data. The component `rx.upload` let your users upload files on the server. Here is a basic example of how it is used: ```python def index(): return rx.fragment( rx.upload(rx.text("Upload files"), rx.icon(tag="upload")), rx.button(on_submit=State.) ) ``` For detailed information, see the reference page of the component [here](/docs/library/forms/upload). # Authentication Overview Source: http://localhost:3000/docs/authentication/authentication-overview.md Many apps require authentication to manage users. There are a few different ways to accomplish this in Reflex: We have solutions that currently exist outside of the core framework: 1. Local Auth: Uses your own database: https://github.com/masenf/reflex-local-auth 2. Google Auth: Uses sign in with Google: https://github.com/masenf/reflex-google-auth 3. Captcha: Generates tests that humans can pass but automated systems cannot: https://github.com/masenf/reflex-google-recaptcha-v2 4. Magic Link Auth: A passwordless login method that sends a unique, one-time-use URL to a user's email: https://github.com/masenf/reflex-magic-link-auth 5. Clerk Auth: A community member wrapped this component and hooked it up in this app: https://github.com/TimChild/reflex-clerk-api 6. Descope Auth: Enables authentication with Descope, supporting passwordless, social login, SSO, and MFA: https://github.com/descope-sample-apps/reflex-descope-auth If you're using the AI Builder, you can also use the built-in [Authentication Integrations](/docs/ai/integrations/overview) which include Azure Auth, Google Auth, Okta Auth, and Descope. ## Guidance for Implementing Authentication - Store sensitive user tokens and information in [backend-only vars](/docs/vars/base-vars#backend-only-vars). - Validate user session and permissions for each event handler that performs an authenticated action and all computed vars or loader events that access private data. - All content that is statically rendered in the frontend (for example, data hardcoded or loaded at compile time in the UI) will be publicly available, even if the page redirects to a login or uses `rx.cond` to hide content. - Only data that originates from state can be truly private and protected. - When using cookies or local storage, a signed JWT can detect and invalidate any local tampering. More auth documentation on the way. Check back soon! # Reflex Changelog Source: http://localhost:3000/docs/changelog.md ## v0.9.7 (2026-07-15) ### Features - Added `default_color_mode` to `rx.Config` (`"system"`, `"light"`, or `"dark"`, also settable via `REFLEX_DEFAULT_COLOR_MODE`), so apps can set the initial color mode — and use the built-in color mode switcher and `rx.color_mode_cond` — without pulling in the large Radix themes CSS. The value drives both the compiled `ThemeProvider` default and the pre-hydration preload script, so there is no flash of the wrong theme on first paint. An explicit `rx.theme(appearance=...)` still takes precedence. ([#6716](https://github.com/reflex-dev/reflex/issues/6716)) - `@rx.memo` components now compile with a configurable JS wrapper: React's `memo` remains the default, `wrapper=` swaps in a custom function `Var` whose imports ride along into the generated module, and `wrapper=None` emits the bare function component. ([#6730](https://github.com/reflex-dev/reflex/issues/6730)) - The new `frozen_lockfile` config option is now honored during frontend package installation: when enabled (the default), bun's initial install runs with `--frozen-lockfile` so a lockfile out of sync with `package.json` fails fast. Set `frozen_lockfile=False` to let the lockfile update in place instead. npm has no equivalent install flag today, so the option is a no-op there. ([#6763](https://github.com/reflex-dev/reflex/issues/6763)) ### Bug Fixes - Fix stateful pages being evaluated twice in one process (forked prod workers and same-process export+serve), which created duplicate `ComponentState` classes and broke frontend hydration (`TypeError: d is not a function`). ([#6710](https://github.com/reflex-dev/reflex/issues/6710)) - Reset the disk state manager write queue task after close. ([#6715](https://github.com/reflex-dev/reflex/issues/6715)) - Close the `RedisTokenManager` redis client and cancel its pub/sub background tasks on app shutdown, fixing leaked redis connections (`ResourceWarning: unclosed Connection`) when the server stops. ([#6724](https://github.com/reflex-dev/reflex/issues/6724)) - Event handlers and computed vars inherited from a state mixin now preserve the source function's custom attributes and keyword-only defaults. ([#6725](https://github.com/reflex-dev/reflex/issues/6725)) ### Performance - Run anonymous telemetry collection and delivery on a dedicated single-worker background thread instead of inline on the asyncio event loop. The blocking syscalls, subprocess calls and synchronous HTTP request used to gather and post an event no longer stall the event loop — notably when reporting backend errors at a high rate. Delivery is best-effort and any failure is suppressed, so telemetry can never affect the running app. ([#6626](https://github.com/reflex-dev/reflex/issues/6626)) - Event chaining (`yield OtherState.handler(rows)`) no longer deep-copies payload values that are not attached to any state: only state-bound `MutableProxy` subtrees are copied, making proxy-free payloads ~5x faster to chain. ([#6739](https://github.com/reflex-dev/reflex/issues/6739)) - `Var.to()` and `Var.guess_type()` resolve their target Var subclass through cached registry lookups instead of scanning the full registry with `safe_issubclass` on every call. ([#6742](https://github.com/reflex-dev/reflex/issues/6742)) ## v0.9.6 (2026-06-25) ### Features - Auto-memoized (`rx.memo`) components now compile to `.web/app_components/` output paths that mirror their defining Python source module (using the real package name, including framework packages) instead of being bundled into a single shared `components.jsx`. The compiler's auto-memo registry is scoped per source module, so identical-rendering subtrees in different modules each emit their own output instead of one silently overwriting another, hot-reloads of a module refresh the correct output, and stale memo files are cleaned up when their source changes. Memos whose module can't be mirrored (`__main__`, unsafe names) fall back to one file per memo at `.web/utils/components/.jsx`. Each mirrored memo's generated export name also carries a stable per-module suffix, so two memos that share a name in different modules compile to distinct symbols and can be used together on one page without colliding. ([#6457](https://github.com/reflex-dev/reflex/issues/6457)) - `rx._x.hybrid_property` now works on dataclasses, pydantic models and SQLAlchemy models, not just `State` classes. Accessing the property through an object var on the frontend (e.g. `State.info.a_b`) renders it as a var, using the same code you already use on the backend. ([#6617](https://github.com/reflex-dev/reflex/issues/6617)) - `reflex init` now writes a Reflex-managed section into `AGENTS.md` (fetched from the canonical source and delimited by markers that preserve surrounding user content), and bridges it for Claude Code by creating a `CLAUDE.md` importing `@AGENTS.md` — or, if a `CLAUDE.md` exists without the import, managing the section there directly. ([#6620](https://github.com/reflex-dev/reflex/issues/6620)) - `rx._x.hybrid_property` now raises a clear error when its frontend logic reads a backend (underscore-prefixed) state var, instead of silently baking the var's server-side default into the frontend. Reference a regular var, or provide a separate frontend implementation with `@.var`. ([#6621](https://github.com/reflex-dev/reflex/issues/6621)) ### Bug Fixes - Sync `reflex.lock/package.json` to `.web/package.json` before installing packages to ensure lock file and package.json are aligned. ([#6658](https://github.com/reflex-dev/reflex/issues/6658)) - Avoid re-entering config loading when a `State` subclass is defined in `rxconfig.py`. ([#6662](https://github.com/reflex-dev/reflex/issues/6662)) - Raise minimum dependency versions to pull in security fixes: `starlette>=1.3.1` (Host-header path poisoning, `request.form()` DoS, and UNC-path SSRF), `python-multipart>=0.0.32` (quadratic-time querystring DoS, unbounded header field size, and negative `Content-Length` buffering in `parse_form`), and `granian>=2.7.4` (WSGI and WebSocket header-panic DoS). ([#6665](https://github.com/reflex-dev/reflex/issues/6665)) - Fixed `modify_state` to rebind `EventContext.token` to the token being modified, so delta resolution and computed vars inside shared-state fan-out tasks observe the correct client token rather than the triggering event's inherited context. ([#6673](https://github.com/reflex-dev/reflex/issues/6673)) ## v0.9.5.post2 (2026-06-10) ### Bug Fixes - Allow access to State from `app_wrap` components ([#6651](https://github.com/reflex-dev/reflex/issues/6651)) ## v0.9.5.post1 (2026-06-10) ### Bug Fixes - Bumped minimum `reflex-components-core` dependency to 0.9.5 for compatibility. ## v0.9.5 (2026-06-10) ### Features - `rx.form` `on_submit` handlers can now annotate their form-data parameter with a `TypedDict` (including `typing_extensions.NotRequired` fields). The submitted mapping is accepted by the event-argument type checker, and at component build time the form statically validates that its controls supply every required `TypedDict` field, raising `EventHandlerValueError` — with the missing and present field names — when a required field has no control with a matching static `name`/`id`. Validation is skipped when the form sets an `id` (controls may be associated externally via the HTML `form` attribute) or when any control identifier is a dynamic `Var`. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) - Event handlers attached to JSX literals built outside a component's render scope — such as an `ErrorBoundary`'s `onError` — can now dispatch events. `addEvents` is reached through a module-level import that `EventLoopProvider` populates on each render, so dispatch no longer depends on a `useContext` hook being hoisted into the calling scope. The state and event-loop providers, previously hard-coded in the layout template, are now injected around the app root by the compiler from the `app_wraps` declared on the `Var`s that use them. ([#6447](https://github.com/reflex-dev/reflex/issues/6447)) - Added `App.hydrate_fallback`, a component rendered during the page's hydration window (React Router's `HydrateFallback`) instead of a blank white page. It can also be configured without code through the `hydrate_fallback` config — a dotted import path to a no-arg callable returning a component, settable via the `REFLEX_HYDRATE_FALLBACK` environment variable — with the `App` argument taking precedence. Note that the fallback only covers the hydration window after the JS bundle has loaded, not the initial bundle download. ([#6630](https://github.com/reflex-dev/reflex/issues/6630)) - Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode — taking precedence over the config-derived defaults as well as `REFLEX_HOT_RELOAD_INCLUDE_PATHS` and `REFLEX_HOT_RELOAD_EXCLUDE_PATHS`. ([#6639](https://github.com/reflex-dev/reflex/issues/6639)) ### Bug Fixes - Anonymous telemetry now reports the installation and project identifiers as UUID strings rather than 128-bit integers. PostHog coerced the large integers to floats, discarding all but ~16 significant digits and risking distinct installs or apps being correlated as one. Each identifier is re-encoded to the same value (a UUID carries the same 128 bits), and a one-time PostHog `$create_alias` links an installation's pre-existing history to its new identifier so continuity is preserved. ([#6611](https://github.com/reflex-dev/reflex/issues/6611)) - `scripts/make_pyi.py` is now a proper CLI for maintaining `pyi_hashes.json`: `--force` regenerates every default target (ignoring the incremental markers), explicit targets are merged into the registry instead of pruning it, and an unreachable last-run commit (after a branch switch or rebase) triggers a full regeneration. A new `--check` mode, wired into the pre-commit CI job, fails when a `pyi_hashes.json` entry no longer has a matching `.py` source. ([#6614](https://github.com/reflex-dev/reflex/issues/6614)) - `State.get_var_value()` no longer silently returns a wrong value when passed a Var operation — an arithmetic/concatenation expression such as `State.a + State.b`, or an indexed/item access such as `State.items[0]`. Previously it resolved the state and field of the operation's *first* operand and returned that field's value instead of the operation's result. It now raises `UnretrievableVarValueError`, consistent with how it already handled vars not associated with any state. Plain field and computed-var references continue to resolve as before. ([#6633](https://github.com/reflex-dev/reflex/issues/6633)) ### Performance - Speed up reading mutable state vars (lists, dicts, dataclasses) through `MutableProxy`. The per-element check that detects `dataclasses.asdict`/`astuple` recursion now reads `frame.f_code.co_filename` directly instead of calling `inspect.getfile()`, cutting proxy read overhead by roughly 3-4x on large containers without changing behavior. ([#6600](https://github.com/reflex-dev/reflex/issues/6600)) ### Miscellaneous - Report the versions of the first-party Reflex subpackages shipped with Reflex (`reflex-base`, the `reflex-components-*` family and `reflex-hosting-cli`) in anonymous telemetry via a new `reflex_package_version` field. The set is derived from Reflex's own declared dependencies, so unrelated third-party `reflex-*` packages are never reported. Now that Reflex is split across many independently-versioned packages, the single `reflex_version` field no longer reflects the full install. ([#6610](https://github.com/reflex-dev/reflex/issues/6610)) ## v0.9.4 (2026-06-03) ### Deprecations - `rx._x.memo` is deprecated in favor of `rx.memo`. The old name remains a working alias for now; update imports to use `rx.memo` directly. ([#6517](https://github.com/reflex-dev/reflex/issues/6517)) - `@rx.memo` now expects each parameter to be annotated as `rx.Var[...]` (or `rx.RestProp`/`rx.EventHandler`) and the function to declare an `rx.Component` or `rx.Var[...]` return type. Memos that still use bare Python types (e.g. `name: str`) or omit the return annotation keep working — the values are coerced to `rx.Var[...]`/`rx.Component` and a deprecation warning points at the parameters and return type that need explicit annotations — but this fallback will be removed in 1.0. ([#6598](https://github.com/reflex-dev/reflex/issues/6598)) ### Features - Added `rx._x.hybrid_property`, a property decorator usable on State classes that works like a normal Python property for backend access while also rendering on the frontend at class level. Use the same method for both, or register a separate frontend implementation with `@.var`. ([#3806](https://github.com/reflex-dev/reflex/issues/3806)) - Promoted the component memo system to a first-class `rx.memo` API. Memo-decorated components now accept `rx.EventHandler` parameters and carry annotated return types so they type-check correctly at call sites. ([#6517](https://github.com/reflex-dev/reflex/issues/6517)) - Added `rx.EMPTY_VAR_COMPONENT`, an empty-component `rx.Var[rx.Component]` sentinel for use as a default on `@rx.memo` `children` slots (and any `rx.Var[rx.Component]` prop) — the component counterpart to `rx.EMPTY_VAR_STR` and `rx.EMPTY_VAR_INT`. ([#6598](https://github.com/reflex-dev/reflex/issues/6598)) - `@rx.memo` now evaluates the decorated function body lazily — on first use (component instantiation) or at compile time — instead of at import time. This speeds up startup and lets a memo reference modules that aren't fully imported yet, sidestepping circular-import errors during decoration. Body-dependent errors (e.g. a var-returning memo that uses hooks or non-bundled imports) now surface when the memo is first used or compiled rather than at import. ([#6598](https://github.com/reflex-dev/reflex/issues/6598)) ### Miscellaneous - Introduced towncrier-based changelog management. Each PR that changes package source now adds a fragment under the affected package's `news/` directory; fragments are assembled into `CHANGELOG.md` at release time. See CONTRIBUTING.md for the full workflow. ([#6350](https://github.com/reflex-dev/reflex/issues/6350)) - Removed the "choose templates" option from `reflex init`. The interactive prompt now offers only a blank app or the AI builder, and no longer opens the open-source templates page. ([#6592](https://github.com/reflex-dev/reflex/issues/6592)) # reflex-base Changelog Source: http://localhost:3000/docs/changelog/reflex-base.md ## v0.9.7 (2026-07-15) ### Deprecations - `ArrayVar.foreach` is deprecated; use `ArrayVar.map` instead. ([#6701](https://github.com/reflex-dev/reflex/issues/6701)) ### Features - `ArrayVar` gained `map`, `filter`, `reduce`, and `flat_map` operations, and `StringVar.strip` now accepts a `chars` argument alongside new `lstrip`/`rstrip` methods. ([#6701](https://github.com/reflex-dev/reflex/issues/6701)) - Added `default_color_mode` to `rx.Config` (`"system"`, `"light"`, or `"dark"`, also settable via `REFLEX_DEFAULT_COLOR_MODE`) and moved the shared `LiteralColorMode` type and color-mode string constants into `reflex_base.constants`. This lets apps set the initial color mode without depending on the Radix themes appearance prop. ([#6716](https://github.com/reflex-dev/reflex/issues/6716)) - `@rx.memo` now accepts a `wrapper=` argument controlling the JS function that wraps the compiled component definition: keep the default React `memo`, pass a custom function `Var` (e.g. an `rx.vars.FunctionStringVar` carrying its own imports), or pass `wrapper=None` to export the bare function component. ([#6730](https://github.com/reflex-dev/reflex/issues/6730)) - Added `frozen_lockfile` to `rx.Config` (default `True`, also settable via `REFLEX_FROZEN_LOCKFILE`), controlling whether the frontend package manager runs in lockfile-enforcing mode. Reflex still creates, manages, and syncs the lockfile regardless; the option only controls whether a lockfile/`package.json` mismatch is treated as an error. ([#6763](https://github.com/reflex-dev/reflex/issues/6763)) ### Bug Fixes - Custom attributes set on a `Field` are now preserved (deep-copied) when the state metaclass rebuilds fields, instead of being silently discarded. The reserved `annotation` attribute is never carried over so rebuilt fields are not misidentified as pydantic fields. ([#6726](https://github.com/reflex-dev/reflex/issues/6726)) - Fix `_get_all_hooks_internal` mutating each component's cached internal hooks with its descendants' hooks, which made memo tag hashes order-dependent and duplicated hooks into memo bodies. ([#6741](https://github.com/reflex-dev/reflex/issues/6741)) ### Performance - Cache framework-path checks in `console.deprecate`'s call-stack walk, making repeat calls ~150x faster (deprecated attributes on hot paths, like `RouterData.page`, no longer cost multiple milliseconds per access). ([#6736](https://github.com/reflex-dev/reflex/issues/6736)) - Event chaining (`yield OtherState.handler(rows)`) no longer deep-copies payload values that are not attached to any state: only state-bound `MutableProxy` subtrees are copied, making proxy-free payloads ~5x faster to chain. ([#6739](https://github.com/reflex-dev/reflex/issues/6739)) - `Var.to()` and `Var.guess_type()` resolve their target Var subclass through cached registry lookups instead of scanning the full registry with `safe_issubclass` on every call (~70% of the cost of constructing a var operation). ([#6742](https://github.com/reflex-dev/reflex/issues/6742)) ## v0.9.6.post1 (2026-06-26) ### Features - Added the `REFLEX_EXTRA_PLUGINS` environment variable, a colon-separated list of fully qualified plugin import paths appended to the config's `plugins` list. Unlike `REFLEX_PLUGINS`, which replaces the list entirely, this preserves plugins configured in `rxconfig.py`; an entry is skipped when a plugin of the same type is already present or when its type is listed in `disable_plugins`. ([#6685](https://github.com/reflex-dev/reflex/issues/6685)) ### Bug Fixes - Stop warning when a non-built-in plugin is listed in `disable_plugins`, so config can opt out of an env-provided plugin without a spurious warning. ([#6685](https://github.com/reflex-dev/reflex/issues/6685)) - Improve error message when plugin spec from environment cannot be used. ([#6685](https://github.com/reflex-dev/reflex/issues/6685)) ## v0.9.6 (2026-06-25) ### Features - `StringVar` now includes `lstrip` and `rstrip` methods. The `strip` method now accepts an optional `chars` argument for consistency with Python’s str API. ([#5417](https://github.com/reflex-dev/reflex/issues/5417)) - Added `reflex_base.utils.memo_paths`, which translates a memo's Python source module into the mirrored `.web/app_components/` JSX path and `$/...` library specifier used by the compiler. The memo component and compiler plugin now route each memo's compiled output through these helpers so it lands alongside its source module's layout, falling back to the per-name `utils/components/` path when the module can't be mirrored. The helpers also derive a per-module-unique JS symbol for each mirrored memo, and the memo registry is keyed by `(name, source module)` so same-named memos defined in different modules coexist instead of colliding. ([#6457](https://github.com/reflex-dev/reflex/issues/6457)) - `ObjectVar` attribute access now resolves `HybridProperty` descriptors defined on the underlying type, evaluating the property's frontend logic with the object var substituted as `self`. `HybridProperty` moved to `reflex_base.vars.hybrid_property` (still available as `rx._x.hybrid_property`). ([#6617](https://github.com/reflex-dev/reflex/issues/6617)) - Add `AgentsMd` constants (canonical URL, managed-section markers, and `CLAUDE.md` bridge) supporting `reflex init` AGENTS.md generation. ([#6620](https://github.com/reflex-dev/reflex/issues/6620)) - Added `HybridPropertyError`, raised when a hybrid property's frontend logic accesses a backend (underscore-prefixed) var on a state while building its frontend var. ([#6621](https://github.com/reflex-dev/reflex/issues/6621)) - `package_json_template` accepts `**additional_keys` to include extra fields (e.g. `name`, `packageManager`, `engines`) in the rendered package.json. ([#6658](https://github.com/reflex-dev/reflex/issues/6658)) ### Bug Fixes - Preserve extra bound event arguments when `rx.upload_files` is used in an upload handler. ([#5290](https://github.com/reflex-dev/reflex/issues/5290)) - Avoid re-entering config loading when a `State` subclass is defined in `rxconfig.py`. ([#6662](https://github.com/reflex-dev/reflex/issues/6662)) - Bump the bundled `vite` dev dependency to 8.0.16, fixing a `server.fs.deny` bypass on Windows alternate paths (CVE-2026-53571) in the dev server of generated apps. ([#6665](https://github.com/reflex-dev/reflex/issues/6665)) - `pyi_hashes.json` entries are now computed from the final `.pyi` content after `ruff format` / `ruff check --fix` post-processing, instead of the intermediate generator output. A pyi_generator change that only affects pre-format output no longer flags hash changes for stubs whose final content is identical. ### Miscellaneous - `Component` gained a private `_get_tag_name()` helper returning the JS expression that references the component's tag (quoted for global-scope DOM tags without a library); `Component._render` and `DebounceInput` now share it instead of duplicating the quoting logic. ([#6637](https://github.com/reflex-dev/reflex/issues/6637)) ## v0.9.5 (2026-06-10) ### Features - Event-argument type checking now treats a mapping-style payload as compatible with a `TypedDict`-annotated callback parameter, scoped narrowly to `on_submit` triggers whose payload is a `Mapping[str, ...]` so unrelated mapping events are unaffected. Adds the `FORM_SUBMIT_MAPPING` type var (exposed on the event namespace and `pyi_generator`'s default imports) and a `Component._is_form_control` class marker that a component sets to declare it contributes a named field to form submission data. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) - `VarData` gained an `app_wraps` field so a `Var` can declare the app-level wrapper components it requires; the compiler injects them around the app root, deduped by `(priority, tag)`. This is how the state and event-loop providers now reach the React tree, since event dispatch reaches `addEvents` via a module-level import (`Imports.EVENTS`) rather than a hoisted hook. The still-reactive `connectErrors` value moves to its own `CONNECT_ERRORS` import/hook, and `Component` deep copies now drop the render cache so compile-time clones (e.g. the app-root wrapper chain) render their mutated children. ([#6447](https://github.com/reflex-dev/reflex/issues/6447)) - Added a `hydrate_fallback` config option (settable via the `REFLEX_HYDRATE_FALLBACK` environment variable), a dotted import path to a callable returning the component shown while the page is hydrating. The app root template now emits a React Router `HydrateFallback` export when a fallback is provided, and the import-path resolution shared with `extra_overlay_function` resolves nested module paths correctly. ([#6630](https://github.com/reflex-dev/reflex/issues/6630)) - Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode. ([#6639](https://github.com/reflex-dev/reflex/issues/6639)) ### Bug Fixes - The `reflex_base.utils.pyi_generator` build-hook entrypoint no longer rewrites `pyi_hashes.json`: it only emits the `.pyi` stubs bundled in the wheel, so building a component package (or the build triggered by `uv sync`) no longer wipes the hash registry down to a single package's entries. The scanner also tolerates source-less modules (e.g. an empty `__init__.py`) instead of raising `OSError` on Python < 3.13. ([#6614](https://github.com/reflex-dev/reflex/issues/6614)) - Fixed `State.router.url` reflecting a stale query string after the URL was changed with `window.history.replaceState`/`pushState` (e.g. from `rx.call_script`). React Router's location does not observe direct history manipulation, so the query and hash are now read from the live `window.location` when building `router_data`, and the next event sent to the backend reports the correct URL (the path stays basename-relative so `frontend_path` is not applied twice; embedded apps keep using the in-widget memory router). A direct history mutation is intentionally not a navigation and does not itself emit an event — use `rx.redirect(..., replace=True)` when you need the URL change to update the router reactively and trigger `on_load`. ([#6625](https://github.com/reflex-dev/reflex/issues/6625)) - pyi_generator no longer includes underscore-prefixed props in generated .pyi files. ([#6628](https://github.com/reflex-dev/reflex/issues/6628)) - Frontend-only events (e.g. `rx.toast`, `rx.redirect`) returned from a middleware's `preprocess` are now emitted to the client instead of being enqueued on the backend event queue, where they had no registered handler and raised `KeyError`. The frontend/backend split that already applied to handler-yielded events is now shared via a `_route_events` helper and applied to middleware-preprocess updates too. ([#6644](https://github.com/reflex-dev/reflex/issues/6644)) ### Performance - Speed up component creation by resolving field defaults lazily (via class-level descriptors) instead of eagerly on every instance, caching each component class's event triggers, and memoizing `to_camel_case`. ([#6576](https://github.com/reflex-dev/reflex/issues/6576)) ## v0.9.4 (2026-06-03) ### Deprecations - Component-returning `@rx.memo` again accepts `key` without an `rx.RestProp` (with a deprecation warning), so `rx.foreach` call sites that set the react `key` keep working; this fallback is removed in 1.0. Other base props (`id`, `class_name`, `style`, `custom_attrs`, `ref`) and identity fields like `tag`/`library` still raise — declare an `rx.RestProp` to forward them. ### Features - Dependency tracking now follows through hybrid properties, so computed vars that read a `hybrid_property` correctly recompute when the underlying state vars change. ([#3806](https://github.com/reflex-dev/reflex/issues/3806)) - The component memo implementation now lives in `reflex_base.components.memo` and is exported as `rx.memo`. Added `EMPTY_VAR_STR` and `EMPTY_VAR_INT` sentinel vars as memo-friendly defaults. ([#6517](https://github.com/reflex-dev/reflex/issues/6517)) ### Bug Fixes - Pin `es-toolkit@1.46.1` via package overrides to work around upstream breakage in recharts and es-toolkit. ([#6570](https://github.com/reflex-dev/reflex/issues/6570)) - Unpin `es-toolkit@1.46.1` via package overrides and bump vite to 8.0.14 to work around upstream breakage in recharts and es-toolkit. ([#6571](https://github.com/reflex-dev/reflex/issues/6571)) ### Miscellaneous - Removed the `Templates.CHOOSE_TEMPLATES` and `Templates.REFLEX_TEMPLATES_URL` constants, which supported the now-removed open-source templates `reflex init` option. ([#6592](https://github.com/reflex-dev/reflex/issues/6592)) # reflex-components-core Changelog Source: http://localhost:3000/docs/changelog/reflex-components-core.md ## v0.9.7 (2026-07-15) ### Miscellaneous - `rx.upload` internals use `ArrayVar.map` instead of the deprecated `foreach`. ([#6701](https://github.com/reflex-dev/reflex/issues/6701)) ## v0.9.6 (2026-06-25) ### Bug Fixes - Deliver extra bound handler arguments to upload handlers, so `on_drop=State.handle_upload(rx.upload_files(...), field)` passes `field` through to the backend instead of raising a missing-argument error. ([#5290](https://github.com/reflex-dev/reflex/issues/5290)) - Preserve literal string types through `rx.cond`, so `rx.cond(State.flag, "green", "red")` infers `Var[Literal["green", "red"]]` instead of widening to `Var[str]` and tripping Pyright on props typed as `Literal[...] | Var[Literal[...]]` such as Radix `color_scheme`. ([#6545](https://github.com/reflex-dev/reflex/issues/6545)) - `rx.debounce_input` no longer crashes the page with `ReferenceError: input is not defined` when wrapping a native DOM element such as `rx.el.input` or `rx.el.textarea`. The `element` prop now passes global-scope tags as string literals (`element:"input"`), while library components keep referencing their imported identifiers. ([#6637](https://github.com/reflex-dev/reflex/issues/6637)) ## v0.9.5 (2026-06-10) ### Features - `Form` now validates statically-knowable fields against a `TypedDict`-annotated `on_submit` handler at create time: it walks nested form controls (including components nested in props), collects their static `name`/`id` values, and raises `EventHandlerValueError` listing the missing and present fields when a required `TypedDict` field has no matching control. `input`, `select`, and `textarea` are marked as form controls so their identifiers are collected, and required-field resolution honors `NotRequired` across Python 3.10 and 3.11+. The `on_submit` handler signature also accepts a mapping-style payload via `on_submit_mapping_event`. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) ### Miscellaneous - The connection-error banner now subscribes only to the dedicated `CONNECT_ERRORS` hook instead of the shared events hook, and the upload component declares its `UploadFilesProvider` through `VarData.app_wraps` rather than `Upload._get_app_wrap_components`. ([#6447](https://github.com/reflex-dev/reflex/issues/6447)) # reflex-components-lucide Changelog Source: http://localhost:3000/docs/changelog/reflex-components-lucide.md ## v1.0.2 (2026-06-10) ### Bug Fixes - `rx.icon` with a static name now emits a deep per-icon import (`import LucideWifiOff from "lucide-react/dist/esm/icons/wifi-off.mjs"`) instead of a named import from the `lucide-react` barrel. The barrel import was harmless on its own, but once an app's module graph also contained `lucide-react/dynamic.mjs` (pulled in by dynamic `Var`-tagged icons, and by the connection overlay mounted on every page), esbuild's dev dep-optimizer rewrote the barrel to statically import every icon chunk — making each page load fetch the entire ~1700-icon library. Deep imports load only the icons actually used. The dynamic `Var` path still resolves through `lucide-react/dynamic.mjs` and is unchanged. ([#6628](https://github.com/reflex-dev/reflex/issues/6628)) # reflex-components-markdown Changelog Source: http://localhost:3000/docs/changelog/reflex-components-markdown.md ## v0.9.3 (2026-06-03) ### Miscellaneous - Updated markdown custom-code collection to use the new `rx.memo` component API instead of the removed `CustomComponent` handling. ([#6517](https://github.com/reflex-dev/reflex/issues/6517)) # reflex-components-moment Changelog Source: http://localhost:3000/docs/changelog/reflex-components-moment.md ## v0.9.2 (2026-06-25) ### Bug Fixes - `rx.moment` now pins its `moment-timezone` dependency to the exact version `0.6.2` instead of a caret range, keeping frontend installs reproducible. ([#6649](https://github.com/reflex-dev/reflex/issues/6649)) # reflex-components-plotly Changelog Source: http://localhost:3000/docs/changelog/reflex-components-plotly.md ## v0.9.3 (2026-06-25) ### Features - `rx.plotly` (and its dist variants like `rx.plotly.basic`) now accept a `locale` prop to localize Plotly's number/date formatting and modebar labels. The matching locale data from `plotly.js-locales` is resolved and merged into the chart config at render time, so per-chart locales work without any manual setup. ([#6428](https://github.com/reflex-dev/reflex/issues/6428)) # reflex-components-radix Changelog Source: http://localhost:3000/docs/changelog/reflex-components-radix.md ## v0.9.6 (2026-07-15) ### Miscellaneous - `rx.segmented_control` internals use `ArrayVar.map` instead of the deprecated `foreach`. ([#6701](https://github.com/reflex-dev/reflex/issues/6701)) ## v0.9.5 (2026-06-10) ### Miscellaneous - Mark the Radix form controls — checkbox, checkbox group, radio group, radio cards, select, switch, and both sliders — with `_is_form_control` so their static `name`/`id` is collected when a form validates its fields against a `TypedDict`-annotated `on_submit` handler. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) ## v0.9.4 (2026-06-03) No significant changes. # reflex-docgen Changelog Source: http://localhost:3000/docs/changelog/reflex-docgen.md ## v0.9.3 (2026-07-15) ### Bug Fixes - Fixed `meta_description` and `description` frontmatter keys being misinterpreted as component preview lambdas. ([#6704](https://github.com/reflex-dev/reflex/issues/6704)) ## v0.9.2 (2026-06-03) No significant changes. # reflex-enterprise Changelog Source: http://localhost:3000/docs/changelog/reflex-enterprise.md ## [0.9.2] - 2026-07-06 ### Added - Add Highcharts chart component (#182) - Add `reflex_app` pytest plugin for downstream testability (#179) ### Changed - Auth: skip protected-var redelivery on same-user navigation (#187) - Add logging to silent auth failure/logout paths (#188) ### Fixed - Auth: redeliver protected vars on public pages (#186) ## [0.9.1] - 2026-07-01 - Add `reflex_enterprise.AuthPlugin` providing integrated secure-by-default auth primitives for protecting pages, fields, vars, and event handlers. See the [full docs](https://reflex.dev/docs/enterprise/auth/overview) for usage. ## [0.9.0] - 2026-06-09 ### Changed - Restrict `reflex export` and `reflex run --env prod` to paid tiers (#177) ## [0.8.3] - 2026-06-01 ### Fixed - Handle map layer `event_handlers` prop properly (#175) - OIDC: use `self.__class__` instead of `type(self)` for handler lookups (#176) - Redo `redirect_to_login` when userinfo is unavailable (#174) ## [0.8.2] - 2026-05-19 ### Added - `InteractiveLeafletLayer` and enhanced event handling for map components (#170) - `postMessage`-based token sync for iframed OIDC popup flows (#172) ### Changed - Harden GitHub Actions workflows (#171) ### Fixed - OIDC: try refresh token before resetting auth on validation failure (#173) ## [0.8.1] - 2026-05-11 ### Fixed - Always set `cls._has_registered_endpoints = True` (#169) ## [0.8.0] - 2026-05-08 ### Changed - `HTTPCookie`: remove `BaseState` patches and fix subclass var dependencies (#164) - Remove runtime version checks throughout the package (#168) ## [0.7.3] - 2026-05-01 ### Added - Add valid issuers and related cleanup (#163) ### Fixed - Avoid race condition between updating the `localStorage` value and redirect (#167) - Fix backend path handling for HTTP cookies (#166) ## [0.7.2] - 2026-04-27 ### Fixed - `messageListener.js`: reference `public_path` in the wrapped component (#162) ## [0.7.1.post2] - 2026-04-27 ### Changed - Remove the GitHub Packages upload step and attach the wheel to the GitHub Release instead ## [0.7.1] - 2026-04-24 ### Added - Add event handlers as HTTP endpoints and expose them via the OpenAPI spec (#156) - Add license URL (#161) ## [0.7.0.post1] - 2026-04-21 ### Fixed - Fix importable path for Mantine (#160) ## [0.7.0] - 2026-04-20 ### Changed - Reflex 0.9 compatibility: use `importable_path` for assets in 0.9.0+ (#159) - Additional Reflex 0.9 compatibility fixes (#157, #158) ## [0.6.5] - 2026-03-27 ### Changed - Replace AWS CodeArtifact with GitHub Packages for the enterprise wheel (#154) - Remove `rx.Base` for 0.9 compatibility (#155) ## [0.6.4] - 2026-03-18 ### Added - Expose display name (#152) ### Changed - Improve token type validation (#153) ## [0.6.3] - 2026-03-16 ### Changed - Raise with an error message (#151) ## [0.6.2] - 2026-03-10 ### Fixed - Redirect after logout consistently (#150) ## [0.6.1] - 2026-03-09 ### Added - Add APIs for Reflex Enterprise to override UI (#149) ## [0.6.0] - 2026-03-03 ### Changed - OIDC: reduce and centralize out-of-band events (#148) ## [0.5.5] - 2026-02-21 ### Changed - OIDC: handle local storage updates internally (#147) ### Fixed - Ignore refresh access token errors (#142) ## [0.5.4] - 2026-02-19 ### Fixed - OIDC: reset auth for invalid tokens in `userinfo` (#146) ## [0.5.3] - 2026-02-19 ### Added - OIDC: allow a pluggable `_http_client` implementation (#145) ## [0.5.2.post1] - 2026-02-18 ### Fixed - Also save `redirect_to_url` in `sessionStorage` (#144) ## [0.5.2] - 2026-02-18 ### Changed - Store PKCE identifiers in `sessionStorage` (#143) ## [0.5.1] - 2026-02-17 ### Fixed - Redirect to login with an invalid token (#141) ## [0.5.0] - 2026-02-13 ### Added - Implement refresh token in `OIDCAuthState` (#139) ### Changed - Overhaul OIDC implementation for flexibility (#135) - Upgrade dependency pins (#140) ## [0.4.3] - 2026-01-22 ### Changed - Exclude OIDC routes from `/sitemap.xml` (#134) ### Fixed - Fix issuer claim request to support multiple values (#137) ## [0.4.2] - 2026-01-07 ### Added - Add `HTTPCookie` partitioned attribute (#129) ### Changed - Use Starlette `cookie_parser` instead of `SimpleCookie` (#133) - Allow cross-origin cookie sync when `same_site="none"` (#128) - Do not validate cookie domain (#130) - Pre-commit fixes (#131) ## [0.4.1] - 2025-12-19 ### Added - Allow passing arbitrary headers to `HTTPCookie.sync` (#127) ## [0.4.0] - 2025-12-17 ### Added - Implement HTTP Cookie support (#119) - Add `presets` and other props for Mantine date components (#120) - Return the row count in `Datasource` and `SSRMDatasource` (#123) - Row grouping options include the `RowGroupingModule` (#122) - Apply default sort order by `primary_key` (#121) ### Changed - Bump ag-grid to 34.3.1 (#125) - Add upload step to build job (#126) ### Fixed - `args_names` must be a tuple (#124) --------------------------------------------------------------- All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). # reflex-hosting-cli Changelog Source: http://localhost:3000/docs/changelog/reflex-hosting-cli.md ## v0.1.67 (2026-06-17) ### Features - Added a `--service-account` flag to `reflex deploy --gcp`, letting Cloud Run services run as a least-privilege per-service service account instead of the project's default compute SA. ([#6556](https://github.com/reflex-dev/reflex/issues/6556)) - Added `--max-instances`, `--allow-unauthenticated/--no-allow-unauthenticated`, `--env KEY=VALUE`, and `--envfile` flags to `reflex deploy --gcp`, letting you cap Cloud Run autoscaling, deploy a private service, and set environment variables at deploy time. ([#6557](https://github.com/reflex-dev/reflex/issues/6557)) - Added `reflex cloud scan`, which uploads your app source for a Reflex-aware security review and reports security and logic flaws. Supports `--json` output and a `--fail-on` severity gate for CI. ([#6632](https://github.com/reflex-dev/reflex/issues/6632)) # Client Storage Overview Source: http://localhost:3000/docs/client-storage/overview.md ```python exec import reflex as rx ``` You can use the browser's local storage to persist state between sessions. This allows user preferences, authentication cookies, other bits of information to be stored on the client and accessed from different browser tabs. A client-side storage var looks and acts like a normal `str` var, except the default value is either `rx.Cookie` or `rx.LocalStorage` depending on where the value should be stored. The key name will be based on the var name, but this can be overridden by passing `name="my_custom_name"` as a keyword argument. For more information see [Browser Storage](/docs/api-reference/browser-storage/). Try entering some values in the text boxes below and then load the page in a separate tab or check the storage section of browser devtools to see the values saved in the browser. ```python demo exec class ClientStorageState(rx.State): my_cookie: str = rx.Cookie("") my_local_storage: str = rx.LocalStorage("") custom_cookie: str = rx.Cookie(name="CustomNamedCookie", max_age=3600) @rx.event def set_my_cookie(self, value: str): self.my_cookie = value @rx.event def set_my_local_storage(self, value: str): self.my_local_storage = value @rx.event def set_custom_cookie(self, value: str): self.custom_cookie = value def client_storage_example(): return rx.vstack( rx.hstack( rx.text("my_cookie"), rx.input( value=ClientStorageState.my_cookie, on_change=ClientStorageState.set_my_cookie, ), ), rx.hstack( rx.text("my_local_storage"), rx.input( value=ClientStorageState.my_local_storage, on_change=ClientStorageState.set_my_local_storage, ), ), rx.hstack( rx.text("custom_cookie"), rx.input( value=ClientStorageState.custom_cookie, on_change=ClientStorageState.set_custom_cookie, ), ), ) ``` # Conditional Rendering Source: http://localhost:3000/docs/components/conditional-rendering.md ```python exec import reflex as rx ``` Recall from the [basics](/docs/getting-started/basics) that we cannot use Python `if/else` statements when referencing state vars in Reflex. Instead, use the `rx.cond` component to conditionally render components or set props based on the value of a state var. ```md video https://youtube.com/embed/ITOZkzjtjUA?start=6040&end=6463 # Video: Conditional Rendering ``` ```md alert # Check out the API reference for [cond docs](/docs/library/dynamic-rendering/cond). ``` ```python eval rx.box(height="2em") ``` Below is a simple example showing how to toggle between two text components by checking the value of the state var `show`. ```python demo exec class CondSimpleState(rx.State): show: bool = True @rx.event def change(self): self.show = not (self.show) def cond_simple_example(): return rx.vstack( rx.button("Toggle", on_click=CondSimpleState.change), rx.cond( CondSimpleState.show, rx.text("Text 1", color="blue"), rx.text("Text 2", color="red"), ), ) ``` If `show` is `True` then the first component is rendered (in this case the blue text). Otherwise the second component is rendered (in this case the red text). ## Conditional Props You can also set props conditionally using `rx.cond`. In this example, we set the `color` prop of a text component based on the value of the state var `show`. ```python demo exec class PropCondState(rx.State): value: int @rx.event def set_end(self, value: list[int | float]): self.value = value[0] def cond_prop(): return rx.slider( default_value=[50], on_value_commit=PropCondState.set_end, color_scheme=rx.cond(PropCondState.value > 50, "green", "pink"), width="100%", ) ``` ## Var Operations You can use [var operations](/docs/vars/var-operations) with the `cond` component for more complex conditions. See the full [cond reference](/docs/library/dynamic-rendering/cond) for more details. ## Multiple Conditional Statements The [`rx.match`](/docs/library/dynamic-rendering/match) component in Reflex provides a powerful alternative to`rx.cond` for handling multiple conditional statements and structural pattern matching. This component allows you to handle multiple conditions and their associated components in a cleaner and more readable way compared to nested `rx.cond` structures. ```python demo exec from typing import List import reflex as rx class MatchState(rx.State): cat_breed: str = "" animal_options: List[str] = [ "persian", "siamese", "maine coon", "ragdoll", "pug", "corgi", ] @rx.event def set_cat_breed(self, breed: str): self.cat_breed = breed def match_demo(): return rx.flex( rx.match( MatchState.cat_breed, ("persian", rx.text("Persian cat selected.")), ("siamese", rx.text("Siamese cat selected.")), ( "maine coon", rx.text("Maine Coon cat selected."), ), ("ragdoll", rx.text("Ragdoll cat selected.")), rx.text("Unknown cat breed selected."), ), rx.select( [ "persian", "siamese", "maine coon", "ragdoll", "pug", "corgi", ], value=MatchState.cat_breed, on_change=MatchState.set_cat_breed, ), direction="column", gap="2", ) ``` # Convert HTML to Reflex Source: http://localhost:3000/docs/components/html-to-reflex.md To convert HTML, CSS, or any design into Reflex code, use our AI-powered build tool at [Reflex Build](https://build.reflex.dev). Simply paste your HTML, CSS, or describe what you want to build, and our AI will generate the corresponding Reflex code for you. ## How to use Reflex Build 1. Go to [Reflex Build](https://build.reflex.dev) 2. Paste your HTML/CSS code or describe your design 3. The AI will automatically generate Reflex code 4. Copy the generated code into your Reflex application ## Convert Figma file to Reflex Check out this [Notion doc](https://www.notion.so/reflex-dev/Convert-HTML-to-Reflex-fe22d0641dcd4d5c91c8404ca41c7e77) for a walk through on how to convert a Figma file into Reflex code. # Props Source: http://localhost:3000/docs/components/props.md ```python exec import reflex as rx ``` Props modify the behavior and appearance of a component. They are passed in as keyword arguments to a component. ## Component Props There are props that are shared between all components, but each component can also define its own props. For example, the `rx.image` component has a `src` prop that specifies the URL of the image to display and an `alt` prop that specifies the alternate text for the image. ```python demo rx.image( src="https://web.reflex-assets.dev/other/logo.jpg", alt="Reflex Logo", ) ``` Check the docs for the component you are using to see what props are available and how they affect the component (see the `rx.image` [reference](/docs/library/media/image#api-reference) page for example). ## Common Props Components support many standard HTML properties as props. For example: the HTML [id](https://developer.mozilla.org/en-US/docs/Web/API/Element/id) property is exposed directly as the prop `id`. The HTML [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) property is exposed as the prop `class_name` (note the Pythonic snake_casing!). ```python demo rx.box( "Hello World", id="box-id", class_name=[ "class-name-1", "class-name-2", ], ) ``` In the example above, the `class_name` prop of the `rx.box` component is assigned a list of class names. This means the `rx.box` component will be styled with the CSS classes `class-name-1` and `class-name-2`. ## Style Props In addition to component-specific props, most built-in components support a full range of style props. You can use any [CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties) to style a component. ```python demo rx.button( "Fancy Button", border_radius="1em", box_shadow="rgba(151, 65, 252, 0.8) 0 15px 30px -10px", background_image="linear-gradient(144deg,#AF40FF,#5B42F3 50%,#00DDEB)", box_sizing="border-box", color="white", opacity=1, ) ``` See the [styling docs](/docs/styling/overview) to learn more about customizing the appearance of your app. ## Binding Props to State ```md alert warning # Optional: Learn all about [State](/docs/state/overview) first. ``` Reflex apps define [State](/docs/state/overview) classes that hold variables that can change over time. State may be modified in response to things like user input like clicking a button, or in response to events like loading a page. State vars can be bound to component props, so that the UI always reflects the current state of the app. Try clicking the badge below to change its color. ```python demo exec class PropExampleState(rx.State): text: str = "Hello World" color: str = "red" @rx.event def flip_color(self): if self.color == "red": self.color = "blue" else: self.color = "red" def index(): return rx.button( PropExampleState.text, color_scheme=PropExampleState.color, on_click=PropExampleState.flip_color, ) ``` In this example, the `color_scheme` prop is bound to the `color` state var. When the `flip_color` event handler is called, the `color` var is updated, and the `color_scheme` prop is updated to match. # Rendering Iterables Source: http://localhost:3000/docs/components/rendering-iterables.md ```python exec import reflex as rx ``` Recall again from the [basics](/docs/getting-started/basics) that we cannot use Python `for` loops when referencing state vars in Reflex. Instead, use the `rx.foreach` component to render components from a collection of data. For dynamic content that should automatically scroll to show the newest items, consider using the [auto scroll](/docs/library/dynamic-rendering/auto-scroll) component together with `rx.foreach`. ```python demo exec class IterState(rx.State): color: list[str] = [ "red", "green", "blue", ] def colored_box(color: str): return rx.button(color, background_color=color) def dynamic_buttons(): return rx.vstack( rx.foreach(IterState.color, colored_box), ) ``` Here's the same example using a lambda function. ```python def dynamic_buttons(): return rx.vstack( rx.foreach(IterState.color, lambda color: colored_box(color)), ) ``` You can also use lambda functions directly with components without defining a separate function. ```python def dynamic_buttons(): return rx.vstack( rx.foreach( IterState.color, lambda color: rx.button(color, background_color=color) ), ) ``` In this first simple example we iterate through a `list` of colors and render a dynamic number of buttons. The first argument of the `rx.foreach` function is the state var that you want to iterate through. The second argument is a function that takes in an item from the data and returns a component. In this case, the `colored_box` function takes in a color and returns a button with that color. ## For vs Foreach ```md definition # Regular For Loop - Use when iterating over constants. # Foreach - Use when iterating over state vars. ``` The above example could have been written using a regular Python `for` loop, since the data is constant. ```python demo exec colors = ["red", "green", "blue"] def dynamic_buttons_for(): return rx.vstack( [colored_box(color) for color in colors], ) ``` However, as soon as you need the data to be dynamic, you must use `rx.foreach`. ```python demo exec class DynamicIterState(rx.State): color: list[str] = [ "red", "green", "blue", ] def add_color(self, form_data): self.color.append(form_data["color"]) def dynamic_buttons_foreach(): return rx.vstack( rx.foreach(DynamicIterState.color, colored_box), rx.form( rx.input(name="color", placeholder="Add a color"), rx.button("Add"), on_submit=DynamicIterState.add_color, ), ) ``` ## Render Function The function to render each item can be defined either as a separate function or as a lambda function. In the example below, we define the function `colored_box` separately and pass it to the `rx.foreach` function. ```python demo exec class IterState2(rx.State): color: list[str] = [ "red", "green", "blue", ] def colored_box(color: rx.Var[str]): return rx.button(color, background_color=color) def dynamic_buttons2(): return rx.vstack( rx.foreach(IterState2.color, colored_box), ) ``` Notice that the type annotation for the `color` parameter in the `colored_box` function is `rx.Var[str]` (rather than just `str`). This is because the `rx.foreach` function passes the item as a `Var` object, which is a wrapper around the actual value. This is what allows us to compile the frontend without knowing the actual value of the state var (which is only known at runtime). ## Enumerating Iterables The function can also take an index as a second argument, meaning that we can enumerate through data as shown in the example below. ```python demo exec class IterIndexState(rx.State): color: list[str] = [ "red", "green", "blue", ] def create_button(color: rx.Var[str], index: int): return rx.box( rx.button(f"{index + 1}. {color}"), padding_y="0.5em", ) def enumerate_foreach(): return rx.vstack( rx.foreach(IterIndexState.color, create_button), ) ``` Here's the same example using a lambda function. ```python def enumerate_foreach(): return rx.vstack( rx.foreach( IterIndexState.color, lambda color, index: create_button(color, index) ), ) ``` ## Iterating Dictionaries We can iterate through a `dict` using a `foreach`. When the dict is passed through to the function that renders each item, it is presented as a list of key-value pairs `[("sky", "blue"), ("balloon", "red"), ("grass", "green")]`. ```python demo exec class SimpleDictIterState(rx.State): color_chart: dict[str, str] = { "sky": "blue", "balloon": "red", "grass": "green", } def display_color(color: list): # color is presented as a list key-value pairs [("sky", "blue"), ("balloon", "red"), ("grass", "green")] return rx.box(rx.text(color[0]), bg=color[1], padding_x="1.5em") def dict_foreach(): return rx.grid( rx.foreach( SimpleDictIterState.color_chart, display_color, ), columns="3", ) ``` ```md alert warning # Dict Type Annotation. It is essential to provide the correct full type annotation for the dictionary in the state definition (e.g., `dict[str, str]` instead of `dict`) to ensure `rx.foreach` works as expected. Proper typing allows Reflex to infer and validate the structure of the data during rendering. ``` ## Nested examples `rx.foreach` can be used with nested state vars. Here we use nested `foreach` components to render the nested state vars. The `rx.foreach(project["technologies"], get_badge)` inside of the `project_item` function, renders the `dict` values which are of type `list`. The `rx.box(rx.foreach(NestedStateFE.projects, project_item))` inside of the `projects_example` function renders each `dict` inside of the overall state var `projects`. ```python demo exec class NestedStateFE(rx.State): projects: list[dict[str, list]] = [ { "technologies": [ "Next.js", "Prisma", "Tailwind", "Google Cloud", "Docker", "MySQL", ] }, {"technologies": ["Python", "Flask", "Google Cloud", "Docker"]}, ] def get_badge(technology: rx.Var[str]) -> rx.Component: return rx.badge(technology, variant="soft", color_scheme="green") def project_item(project: rx.Var[dict[str, list]]) -> rx.Component: return rx.box( rx.hstack(rx.foreach(project["technologies"], get_badge)), ) def projects_example() -> rx.Component: return rx.box(rx.foreach(NestedStateFE.projects, project_item)) ``` If you want an example where not all of the values in the dict are the same type then check out the example on [var operations using foreach](/docs/vars/var-operations). Here is a further example of how to use `foreach` with a nested data structure. ```python demo exec class NestedDictIterState(rx.State): color_chart: dict[str, list[str]] = { "purple": ["red", "blue"], "orange": ["yellow", "red"], "green": ["blue", "yellow"], } def display_colors(color: rx.Var[tuple[str, list[str]]]): return rx.vstack( rx.text(color[0], color=color[0]), rx.hstack( rx.foreach( color[1], lambda x: rx.box(rx.text(x, color="black"), bg=x), ) ), ) def nested_dict_foreach(): return rx.grid( rx.foreach( NestedDictIterState.color_chart, display_colors, ), columns="3", ) ``` ## Foreach with Cond We can also use `foreach` with the `cond` component. In this example we define the function `render_item`. This function takes in an `item`, uses the `cond` to check if the item `is_packed`. If it is packed it returns the `item_name` with a `✔` next to it, and if not then it just returns the `item_name`. We use the `foreach` to iterate over all of the items in the `to_do_list` using the `render_item` function. ```python demo exec import dataclasses @dataclasses.dataclass class ToDoListItem: item_name: str is_packed: bool class ForeachCondState(rx.State): to_do_list: list[ToDoListItem] = [ ToDoListItem(item_name="Space suit", is_packed=True), ToDoListItem(item_name="Helmet", is_packed=True), ToDoListItem(item_name="Back Pack", is_packed=False), ] def render_item(item: rx.Var[ToDoListItem]): return rx.cond( item.is_packed, rx.list.item(item.item_name + " ✔"), rx.list.item(item.item_name), ) def packing_list(): return rx.vstack( rx.text("Sammy's Packing List"), rx.list(rx.foreach(ForeachCondState.to_do_list, render_item)), ) ``` # Custom Component CLI Reference Source: http://localhost:3000/docs/custom-components/command-reference.md The custom component commands are under `reflex component` subcommand. To see the list of available commands, run `reflex component --help`. To see the manual on a specific command, run `reflex component --help`, for example, `reflex component init --help`. ```bash reflex component --help ``` ```text Usage: reflex component [OPTIONS] COMMAND [ARGS]... Subcommands for creating and publishing Custom Components. Options: --help Show this message and exit. Commands: init Initialize a custom component. build Build a custom component. share Collect more details on the published package for gallery. ``` ## reflex component init Below is an example of running the `init` command. ```bash reflex component init ``` ```text reflex component init ─────────────────────────────────────── Initializing reflex-google-auth project ─────────────────────────────────────── Info: Populating pyproject.toml with package name: reflex-google-auth Info: Initializing the component directory: custom_components/reflex_google_auth Info: Creating app for testing: google_auth_demo ──────────────────────────────────────────── Initializing google_auth_demo ──────────────────────────────────────────── [07:58:16] Initializing the app directory. console.py:85 Initializing the web directory. console.py:85 Success: Initialized google_auth_demo ─────────────────────────────────── Installing reflex-google-auth in editable mode. ─────────────────────────────────── Info: Package reflex-google-auth installed! Custom component initialized successfully! ─────────────────────────────────────────────────── Project Summary ─────────────────────────────────────────────────── [ README.md ]: Package description. Please add usage examples. [ pyproject.toml ]: Project configuration file. Please fill in details such as your name, email, homepage URL. [ custom_components/ ]: Custom component code template. Start by editing it with your component implementation. [ google_auth_demo/ ]: Demo App. Add more code to this app and test. ``` The `init` command uses the current enclosing folder name to construct a python package name, typically in the kebab case. For example, if running init in folder `google_auth`, the package name will be `reflex-google-auth`. The added prefix reduces the chance of name collision on PyPI (the Python Package Index), and it indicates that the package is a Reflex custom component. The user can override the package name by providing the `--package-name` option. The `init` command creates a set of files and folders prefilled with the package name and other details. During the init, the `custom_component` folder is installed locally in editable mode, so a developer can incrementally develop and test with ease. The changes in component implementation is automatically reflected where it is used. Below is the folder structure after the `init` command. ```text google_auth/ ├── pyproject.toml ├── README.md ├── custom_components/ │ └── reflex_google_auth/ │ ├── google_auth.py │ └── __init__.py └── google_auth_demo/ └── assets/ google_auth_demo/ requirements.txt rxconfig.py ``` ### pyproject.toml The `pyproject.toml` is required for the package to build and be published. It is prefilled with information such as the package name, version (`0.0.1`), author name and email, homepage URL. By default the **Apache-2.0** license is used, the same as Reflex. If any of this information requires update, the user can edit the file by hand. ### README The `README.md` file is created with installation instructions, e.g. `pip install reflex-google-auth`, and a brief description of the package. Typically the `README.md` contains usage examples. On PyPI, the `README.md` is rendered as part of the package page. ### Custom Components Folder The `custom_components` folder is where the actual implementation is. Do not worry about this folder name: there is no need to change it. It is where `pyproject.toml` specifies the source of the python package is. The published package contains the contents inside it, excluding this folder. `reflex_google_auth` is the top folder for importable code. The `reflex_google_auth/__init__.py` imports everything from the `reflex_google_auth/google_auth.py`. For the user of the package, the import looks like `from reflex_google_auth import ABC, XYZ`. `reflex_google_auth/google_auth.py` is prefilled with code example and instructions from the [wrapping react guide](/docs/wrapping-react/overview). ### Demo App Folder A demo app is generated inside `google_auth_demo` folder with import statements and example usage of the component. This is a regular Reflex app. Go into this directory and start using any reflex commands for testing. ### Help Manual The help manual is shown when adding the `--help` option to the command. ```bash reflex component init --help ``` ```text Usage: reflex component init [OPTIONS] Initialize a custom component. Args: library_name: The name of the library. install: Whether to install package from this local custom component in editable mode. loglevel: The log level to use. Raises: Exit: If the pyproject.toml already exists. Options: --library-name TEXT The name of your library. On PyPI, package will be published as `reflex-{library- name}`. --install / --no-install Whether to install package from this local custom component in editable mode. [default: install] --loglevel [debug|info|warning|error|critical] The log level to use. [default: LogLevel.INFO] --help Show this message and exit. ``` ## reflex component publish To publish to a package index, a user is required to already have an account with them. As of **0.7.5**, Reflex does not handle the publishing process for you. You can do so manually by first running `reflex component build` followed by `twine upload` or `uv publish` or your choice of a publishing utility. You can then share your build on our website with `reflex component share`. ## reflex component build It is not required to run the `build` command separately before publishing. The `publish` command will build the package if it is not already built. The `build` command is provided for the user's convenience. The `build` command generates the `.tar.gz` and `.whl` distribution files to be uploaded to the desired package index, for example, PyPI. This command must be run at the top level of the project where the `pyproject.toml` file is. As a result of a successful build, there is a new `dist` folder with the distribution files. ```bash reflex component build --help ``` ```text Usage: reflex component build [OPTIONS] Build a custom component. Must be run from the project root directory where the pyproject.toml is. Args: loglevel: The log level to use. Raises: Exit: If the build fails. Options: --loglevel [debug|info|warning|error|critical] The log level to use. [default: LogLevel.INFO] --help Show this message and exit. ``` # Custom Components Overview Source: http://localhost:3000/docs/custom-components/overview.md ```python exec import reflex as rx ``` Reflex users create many components of their own: ready to use high level components, or nicely wrapped React components. With **Custom Components**, the community can easily share these components now. Release **0.4.3** introduces a series of `reflex component` commands that help developers wrap react components, test, and publish them as python packages. As shown in the image below, there are already a few custom components published on PyPI, such as `reflex-spline`, `reflex-webcam`. Check out the custom components gallery [here](/docs/custom-components/overview). ```python eval rx.center( rx.image( src="https://web.reflex-assets.dev/custom_components/pypi_reflex_custom_components.webp", alt="PyPI page listing Reflex custom components", width="400px", border_radius="15px", border="1px solid", ), ) ``` ## Prerequisites for Publishing In order to publish a Python package, an account is required with a python package index, for example, PyPI. The documentation to create accounts and generate API tokens can be found on their websites. For a quick reference, check out our [Prerequisites for Publishing](/docs/custom-components/prerequisites-for-publishing) page. ## Steps to Publishing Follow these steps to publish the custom component as a python package: 1. `reflex component init`: creates a new custom component project from templates. 2. dev and test: developer implements and tests the custom component. 3. `reflex component build`: builds the package. 4. `twine upload` or `uv publish`: uploads the package to a python package index. ### Initialization ```bash reflex component init ``` First create a new folder for your custom component project, for example `color_picker`. The package name will be `reflex-color-picker`. The prefix `reflex-` is intentionally added for all custom components for easy search on PyPI. If you prefer a particular name for the package, you can either change it manually in the `pyproject.toml` file or add the `--library-name` option in the `reflex component init` command initially. Run `reflex component init`, and a set of files and folders will be created in the `color_picker` folder. The `pyproject.toml` file is the configuration file for the project. The `custom_components` folder is where the custom component implementation is. The `color_picker_demo` folder is a demo Reflex app that uses the custom component. If this is the first time of creating python packages, it is encouraged to browse through all the files (there are not that many) to understand the structure of the project. ```bash color_picker/ ├── pyproject.toml <- Configuration file ├── README.md ├── .gitignore <- Exclude dist/ and metadata folders ├── custom_components/ │ └── reflex_color_picker/ <- Custom component source directory │ ├── color_picker.py │ └── __init__.py └── color_picker_demo/ <- Demo Reflex app directory └── assets/ color_picker_demo/ requirements.txt rxconfig.py ``` ### Develop and Test After finishing the custom component implementation, the user is encouraged to fully test it before publishing. The generated Reflex demo app `color_picker_demo` is a good place to start. It is a regular Reflex app prefilled with imports and usage of this component. During the init, the `custom_component` folder is installed locally in editable mode, so a developer can incrementally develop and test with ease. The changes in component implementation are automatically reflected in the demo app. ### Publish ```bash reflex component build ``` Once you're ready to publish your package, run `reflex component build` to build the package. The command builds the distribution files if they are not already built. The end result is a `dist` folder containing the distribution files. The user does not need to do anything manually with these distribution files. In order to publish these files as a Python package, you need to use a publishing utility. Any would work, but we recommend either [Twine](https://twine.readthedocs.io/en/stable/) or (uv)[https://docs.astral.sh/uv/guides/package/#publishing-your-package]. Make sure to keep your package version in pyproject.toml updated. You can also share your components with the rest of the community at our website using the command `reflex component share`. See you there! # Python Package Index Source: http://localhost:3000/docs/custom-components/prerequisites-for-publishing.md ```python exec import reflex as rx image_style = { "width": "400px", "border_radius": "12px", "border": "1px solid var(--secondary-5)", } ``` In order to publish a Python package, you need to use a publishing utility. Any would work, but we recommend either [Twine](https://twine.readthedocs.io/en/stable/) or [uv](https://docs.astral.sh/uv/guides/package/#publishing-your-package). ## PyPI It is straightforward to create accounts and API tokens with PyPI. There is official help on the [PyPI website](https://pypi.org/help/). For a quick reference here, go to the top right corner of the PyPI website and look for the button to register and fill out personal information. ```python eval rx.center( rx.image( src="https://web.reflex-assets.dev/custom_components/pypi_register.webp", alt="PyPI account registration page", style=image_style, margin_bottom="16px", loading="lazy", ), ) ``` A user can use username and password to authenticate with PyPI when publishing. ```python eval rx.center( rx.image( src="https://web.reflex-assets.dev/custom_components/pypi_account_settings.webp", alt="PyPI account settings page", style=image_style, margin_bottom="16px", loading="lazy", ), ) ``` Scroll down to the API tokens section and click on the "Add API token" button. Fill out the form and click "Generate API token". ```python eval rx.center( rx.image( src="https://web.reflex-assets.dev/custom_components/pypi_api_tokens.webp", alt="PyPI API tokens settings page", style=image_style, width="700px", loading="lazy", ), ) ``` # Database Overview Source: http://localhost:3000/docs/database/overview.md Reflex uses [sqlmodel](https://sqlmodel.tiangolo.com) to provide a built-in ORM wrapping SQLAlchemy. The examples on this page refer specifically to how Reflex uses various tools to expose an integrated database interface. Only basic use cases will be covered below, but you can refer to the [sqlmodel tutorial](https://sqlmodel.tiangolo.com/tutorial/select/) for more examples and information, just replace `SQLModel` with `rx.Model` and `Session(engine)` with `rx.session()` For advanced use cases, please see the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/orm/quickstart.html) (v1.4). ```md alert info # Using NoSQL Databases If you are using a NoSQL database (e.g. MongoDB), you can work with it in Reflex by installing the appropriate Python client library. In this case, Reflex will not provide any ORM features. ``` ## Installation The ORM dependencies (SQLModel and Alembic) are an optional extra. Install them with the `db` extra: ```bash pip install "reflex[db]" ``` ## Connecting Reflex provides a built-in SQLite database for storing and retrieving data. You can connect to your own SQL compatible database by modifying the `rxconfig.py` file with your database url. ```python config = rx.Config( app_name="my_app", db_url="sqlite:///reflex.db", ) ``` For more examples of database URLs that can be used, see the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/engines.html#backend-specific-urls). Be sure to install the appropriate DBAPI driver for the database you intend to use. ## Tables To create a table make a class that inherits from `rx.Model` with and specify that it is a table. ```python class User(rx.Model, table=True): username: str email: str password: str ``` ## Migrations Reflex leverages [alembic](https://alembic.sqlalchemy.org/en/latest/) to manage database schema changes. Before the database feature can be used in a new app you must call `reflex db init` to initialize alembic and create a migration script with the current schema. After making changes to the schema, use `reflex db makemigrations --message 'something changed'` to generate a script in the `alembic/versions` directory that will update the database schema. It is recommended that generated scripts be inspected before applying them. Bear in mind that your newest models will not be detected by the `reflex db makemigrations` command unless imported and used somewhere within the application. The `reflex db migrate` command is used to apply migration scripts to bring the database up to date. During app startup, if Reflex detects that the current database schema is not up to date, a warning will be displayed on the console. ## Queries To query the database you can create a `rx.session()` which handles opening and closing the database connection. You can use normal SQLAlchemy queries to query the database. ```python with rx.session() as session: session.add(User(username="test", email="admin@reflex.dev", password="admin")) session.commit() ``` ```md video https://youtube.com/embed/ITOZkzjtjUA?start=6835&end=8225 # Video: Tutorial of Database Model with Forms, Model Field Changes and Migrations, and adding a DateTime Field ``` # Queries Source: http://localhost:3000/docs/database/queries.md Queries are used to retrieve data from a database. A query is a request for information from a database table or combination of tables. A query can be used to retrieve data from a single table or multiple tables. A query can also be used to insert, update, or delete data from a table. ## Session To execute a query you must first create a `rx.session`. You can use the session to query the database using SQLModel or SQLAlchemy syntax. The `rx.session` statement will automatically close the session when the code block is finished. **If `session.commit()` is not called, the changes will be rolled back and not persisted to the database.** The code can also explicitly rollback without closing the session via `session.rollback()`. The following example shows how to create a session and query the database. First we create a table called `User`. ```python class User(rx.Model, table=True): username: str email: str ``` ### Select Then we create a session and query the User table. ```python class QueryUser(rx.State): name: str users: list[User] @rx.event def get_users(self): with rx.session() as session: self.users = session.exec( select(User).where(User.username.contains(self.name)) ).all() ``` The `get_users` method will query the database for all users that contain the value of the state var `name`. ### Insert Similarly, the `session.add()` method to add a new record to the database or persist an existing object. ```python class AddUser(rx.State): username: str email: str @rx.event def add_user(self): with rx.session() as session: session.add(User(username=self.username, email=self.email)) session.commit() ``` ### Update To update the user, first query the database for the object, make the desired modifications, `.add` the object to the session and finally call `.commit()`. ```python class ChangeEmail(rx.State): username: str email: str @rx.event def modify_user(self): with rx.session() as session: user = session.exec( select(User).where((User.username == self.username)) ).first() user.email = self.email session.add(user) session.commit() ``` ### Delete To delete a user, first query the database for the object, then call `.delete()` on the session and finally call `.commit()`. ```python class RemoveUser(rx.State): username: str @rx.event def delete_user(self): with rx.session() as session: user = session.exec( select(User).where(User.username == self.username) ).first() session.delete(user) session.commit() ``` ## ORM Object Lifecycle The objects returned by queries are bound to the session that created them, and cannot generally be used outside that session. After adding or updating an object, not all fields are automatically updated, so accessing certain attributes may trigger additional queries to refresh the object. To avoid this, the `session.refresh()` method can be used to update the object explicitly and ensure all fields are up to date before exiting the session. ```python class AddUserForm(rx.State): user: User | None = None @rx.event def add_user(self, form_data: dict[str, Any]): with rx.session() as session: self.user = User(**form_data) session.add(self.user) session.commit() session.refresh(self.user) ``` Now the `self.user` object will have a correct reference to the autogenerated primary key, `id`, even though this was not provided when the object was created from the form data. If `self.user` needs to be modified or used in another query in a new session, it must be added to the session. Adding an object to a session does not necessarily create the object, but rather associates it with a session where it may either be created or updated accordingly. ```python class AddUserForm(rx.State): ... @rx.event def update_user(self, form_data: dict[str, Any]): if self.user is None: return with rx.session() as session: self.user.set(**form_data) session.add(self.user) session.commit() session.refresh(self.user) ``` If an ORM object will be referenced and accessed outside of a session, you should call `.refresh()` on it to avoid stale object exceptions. ## Using SQL Directly Avoiding SQL is one of the main benefits of using an ORM, but sometimes it is necessary for particularly complex queries, or when using database-specific features. SQLModel exposes the `session.execute()` method that can be used to execute raw SQL strings. If parameter binding is needed, the query may be wrapped in [`sqlalchemy.text`](https://docs.sqlalchemy.org/en/14/core/sqlelement.html#sqlalchemy.sql.expression.text), which allows colon-prefix names to be used as placeholders. ```md alert info # Never use string formatting to construct SQL queries, as this may lead to SQL injection vulnerabilities in the app. ``` ```python import sqlalchemy import reflex as rx class State(rx.State): @rx.event def insert_user_raw(self, username, email): with rx.session() as session: session.execute( sqlalchemy.text( "INSERT INTO user (username, email) VALUES (:username, :email)" ), {"username": username, "email": email}, ) session.commit() @rx.var def raw_user_tuples(self) -> list[list]: with rx.session() as session: return [list(row) for row in session.execute("SELECT * FROM user").all()] ``` ## Writing Efficient Queries Databases are generally much better at filtering, joining, and aggregating than Python. Every row returned by a query is sent over the network and held in memory as part of the state — for every user with an open session. Do the work in the query itself and return only display-ready results. The examples below use a simple `Order` model. ```python class Order(rx.Model, table=True): user_id: int status: str amount: float | None = None ``` ### Aggregate in SQL, not Python Avoid fetching a whole table just to compute a summary in Python. Use the database's aggregate functions (`COUNT`, `SUM`, `AVG`, `MIN`, `MAX`) with `GROUP BY` so only the summary rows come back. ```python from sqlmodel import select, func class OrderStats(rx.State): order_count: int = 0 orders_by_status: list[tuple[str, int]] = [] @rx.event def load_stats(self): with rx.session() as session: # Count rows in the database instead of len() on a full fetch. self.order_count = session.exec(select(func.count(Order.id))).one() # One row per group, ready for a chart or summary table. self.orders_by_status = [ tuple(row) for row in session.exec( select(Order.status, func.count(Order.id)).group_by(Order.status) ).all() ] ``` Related metrics can be computed in a single query with conditional aggregation, instead of issuing one query per metric: ```python import sqlalchemy from sqlmodel import select, func class OrderKPIs(rx.State): total_orders: int = 0 completed_orders: int = 0 total_amount: float = 0.0 @rx.event def load_kpis(self): with rx.session() as session: total, completed, amount = session.exec( select( func.count(Order.id), func.sum( sqlalchemy.case((Order.status == "completed", 1), else_=0) ), func.coalesce(func.sum(Order.amount), 0.0), ) ).one() self.total_orders = total self.completed_orders = completed or 0 self.total_amount = float(amount) ``` ### Avoid Queries Inside Loops Issuing a query per item — the "N+1" pattern — multiplies network round trips. Fetch everything in one statement with a `JOIN`, an `IN (...)` list, or `GROUP BY`. ```python # Efficient: one query for all users at once. with rx.session() as session: orders = session.exec( select(Order).where(Order.user_id.in_([user.id for user in users])) ).all() # Inefficient: one query per user (N+1). with rx.session() as session: for user in users: orders = session.exec(select(Order).where(Order.user_id == user.id)).all() ``` The `.in_()` method binds each value as a separate query parameter, so it is safe to use with user-provided values. ```md alert info # Raw SQL: pass lists with an expanding bind parameter In raw SQL, use an *expanding* bind parameter for an `IN` list — never join the values into the SQL string, which exposes the query to SQL injection. ``` The example below binds the list with an expanding parameter: ```python import sqlalchemy with rx.session() as session: rows = session.execute( sqlalchemy.text("SELECT * FROM users WHERE id IN :user_ids").bindparams( sqlalchemy.bindparam("user_ids", expanding=True) ), {"user_ids": [1, 2, 3]}, ).all() ``` If the related objects are linked with foreign keys, the [relationship loading techniques](/docs/database/relationships) can also fetch linked objects without extra queries. ### Return Only What the UI Needs - Select only the columns the UI displays when tables are wide, instead of whole rows. - Give every query that returns detail rows a `.limit()`, and use offset-based [pagination](/docs/library/tables-and-data-grids/table) when the user needs more rows. - Load static data (dropdown options, date bounds) once in an `on_load` event handler. When a filter changes, re-run only the filtered queries — not the option lookups. - Avoid caching large raw query results in state vars to work around a slow query: per-user state duplicates that data in server memory for every session. Fix the query instead — aggregate in SQL, add a limit, combine round trips. Keeping small, display-ready results in state (chart rows, KPI values, one page of a table) is the intended pattern. ## Handling NULL Values Database columns may contain `NULL`, which arrives in Python as `None`. Aggregates like `SUM` and `AVG` also return `NULL` when they run over zero rows. Casting such results directly will crash: ```python float(row[0]) # TypeError: float() argument must be ... not 'NoneType' ``` There are two ways to handle this. ### Coalesce in the Query (Preferred) `COALESCE` returns its first non-`NULL` argument, so the query itself guarantees a usable value. This is especially important for nullable numeric columns and for aggregates that may run over empty groups. ```python from sqlmodel import select, func with rx.session() as session: total = session.exec(select(func.coalesce(func.sum(Order.amount), 0.0))).one() ``` The same works in raw SQL: `SELECT COALESCE(SUM(amount), 0) FROM ...`. ### Guard in Python When consuming rows that may contain `NULL`s, fall back explicitly while building the values: ```python orders = [ { "status": str(row[0] or ""), "amount": float(row[1]) if row[1] is not None else 0.0, } for row in rows ] ``` ## Async Database Operations Reflex provides an async version of the session function called `rx.asession` for asynchronous database operations. This is useful when you need to perform database operations in an async context, such as within async event handlers. ### Configuring the Async Database URL `rx.asession` needs its own `async_db_url` in `rxconfig.py`. It must point at the same database as `db_url` but use an async driver. Calling `rx.asession()` when it is unset raises an error. ```python config = rx.Config( app_name="my_app", db_url="sqlite:///reflex.db", async_db_url="sqlite+aiosqlite:///reflex.db", ) ``` The matching async DBAPI driver is not part of the `reflex[db]` extra, so install it separately. For the SQLite URL above, install `aiosqlite`: ```bash pip install aiosqlite ``` For PostgreSQL, install `psycopg` (psycopg3). It works as both the sync and async driver, so `db_url` and `async_db_url` can share one `postgresql+psycopg://...` scheme: ```bash pip install "psycopg[binary]" ``` The `rx.asession` function returns an async SQLAlchemy session that must be used with an async context manager. Most operations against the `asession` must be awaited. ```python import sqlalchemy.ext.asyncio import sqlalchemy import reflex as rx class AsyncUserState(rx.State): users: list[User] = [] @rx.event(background=True) async def get_users_async(self): async with rx.asession() as asession: result = await asession.execute(select(User)) async with self: self.users = result.all() ``` ### Async Select The following example shows how to query the database asynchronously: ```python class AsyncQueryUser(rx.State): name: str users: list[User] = [] @rx.event(background=True) async def get_users(self): async with rx.asession() as asession: stmt = select(User).where(User.username.contains(self.name)) result = await asession.execute(stmt) async with self: self.users = result.all() ``` ### Async Insert To add a new record to the database asynchronously: ```python class AsyncAddUser(rx.State): username: str email: str @rx.event(background=True) async def add_user(self): async with rx.asession() as asession: asession.add(User(username=self.username, email=self.email)) await asession.commit() ``` ### Async Update To update a user asynchronously: ```python class AsyncChangeEmail(rx.State): username: str email: str @rx.event(background=True) async def modify_user(self): async with rx.asession() as asession: stmt = select(User).where(User.username == self.username) result = await asession.execute(stmt) user = result.first() if user: user.email = self.email asession.add(user) await asession.commit() ``` ### Async Delete To delete a user asynchronously: ```python class AsyncRemoveUser(rx.State): username: str @rx.event(background=True) async def delete_user(self): async with rx.asession() as asession: stmt = select(User).where(User.username == self.username) result = await asession.execute(stmt) user = result.first() if user: await asession.delete(user) await asession.commit() ``` ### Async Refresh Similar to the regular session, you can refresh an object to ensure all fields are up to date: ```python class AsyncAddUserForm(rx.State): user: User | None = None @rx.event(background=True) async def add_user(self, form_data: dict[str, str]): async with rx.asession() as asession: async with self: self.user = User(**form_data) asession.add(self.user) await asession.commit() await asession.refresh(self.user) ``` ### Async SQL Execution You can also execute raw SQL asynchronously: ```python class AsyncRawSQL(rx.State): users: list[list] = [] @rx.event(background=True) async def insert_user_raw(self, username, email): async with rx.asession() as asession: await asession.execute( sqlalchemy.text( "INSERT INTO user (username, email) VALUES (:username, :email)" ), dict(username=username, email=email), ) await asession.commit() @rx.event(background=True) async def get_raw_users(self): async with rx.asession() as asession: result = await asession.execute("SELECT * FROM user") async with self: self.users = [list(row) for row in result.all()] ``` ```md alert info # Important Notes for Async Database Operations - Always use the `@rx.event(background=True)` decorator for async event handlers - Most operations against the `asession` must be awaited, including `commit()`, `execute()`, `refresh()`, and `delete()` - The `add()` method does not need to be awaited - Result objects from queries have methods like `all()` and `first()` that are synchronous and return data directly - Use `async with self:` when updating state variables in background tasks ``` # Relationships Source: http://localhost:3000/docs/database/relationships.md Foreign key relationships are used to link two tables together. For example, the `Post` model may have a field, `user_id`, with a foreign key of `user.id`, referencing a `User` model. This would allow us to automatically query the `Post` objects associated with a user, or find the `User` object associated with a `Post`. To establish bidirectional relationships a model must correctly set the `back_populates` keyword argument on the `Relationship` to the relationship attribute in the _other_ model. ## Foreign Key Relationships To create a relationship, first add a field to the model that references the primary key of the related table, then add a `sqlmodel.Relationship` attribute which can be used to access the related objects. Defining relationships like this requires the use of `sqlmodel` objects as seen in the example. ```python from typing import List, Optional import sqlmodel import reflex as rx class Post(rx.Model, table=True): title: str body: str user_id: int = sqlmodel.Field(foreign_key="user.id") user: Optional["User"] = sqlmodel.Relationship(back_populates="posts") flags: Optional[List["Flag"]] = sqlmodel.Relationship(back_populates="post") class User(rx.Model, table=True): username: str email: str posts: List[Post] = sqlmodel.Relationship(back_populates="user") flags: List["Flag"] = sqlmodel.Relationship(back_populates="user") class Flag(rx.Model, table=True): post_id: int = sqlmodel.Field(foreign_key="post.id", index=True) user_id: int = sqlmodel.Field(foreign_key="user.id") message: str post: Optional[Post] = sqlmodel.Relationship(back_populates="flags") user: Optional[User] = sqlmodel.Relationship(back_populates="flags") ``` See the [SQLModel Relationship Docs](https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/define-relationships-attributes/) for more details. ## Querying Relationships ### Inserting Linked Objects The following example assumes that the flagging user is stored in the state as a `User` instance and that the post `id` is provided in the data submitted in the form. ```python class FlagPostForm(rx.State): user: User @rx.event def flag_post(self, form_data: dict[str, Any]): with rx.session() as session: post = session.get(Post, int(form_data.pop("post_id"))) flag = Flag(message=form_data.pop("message"), post=post, user=self.user) session.add(flag) session.commit() ``` ### How are Relationships Dereferenced? By default, the relationship attributes are in **lazy loading** or `"select"` mode, which generates a query _on access_ to the relationship attribute. Lazy loading is generally fine for single object lookups and manipulation, but can be inefficient when accessing many linked objects for serialization purposes. There are several alternative loading mechanisms available that can be set on the relationship object or when performing the query. - "joined" or `joinload` - generates a single query to load all related objects at once. - "subquery" or `subqueryload` - generates a single query to load all related objects at once, but uses a subquery to do the join, instead of a join in the main query. - "selectin" or `selectinload` - emits a second (or more) SELECT statement which assembles the primary key identifiers of the parent objects into an IN clause, so that all members of related collections / scalar references are loaded at once by primary key There are also non-loading mechanisms, "raise" and "noload" which are used to specifically avoid loading a relationship. Each loading method comes with tradeoffs and some are better suited for different data access patterns. See [SQLAlchemy: Relationship Loading Techniques](https://docs.sqlalchemy.org/en/14/orm/loading_relationships.html) for more detail. ### Querying Linked Objects To query the `Post` table and include all `User` and `Flag` objects up front, the `.options` interface will be used to specify `selectinload` for the required relationships. Using this method, the linked objects will be available for rendering in frontend code without additional steps. ```python import sqlalchemy class PostState(rx.State): posts: List[Post] @rx.event def load_posts(self): with rx.session() as session: self.posts = session.exec( Post.select.options( sqlalchemy.orm.selectinload(Post.user), sqlalchemy.orm.selectinload(Post.flags).options( sqlalchemy.orm.selectinload(Flag.user), ), ).limit(15) ).all() ``` The loading methods create new query objects and thus may be linked if the relationship itself has other relationships that need to be loaded. In this example, since `Flag` references `User`, the `Flag.user` relationship must be chain loaded from the `Post.flags` relationship. ### Specifying the Loading Mechanism on the Relationship Alternatively, the loading mechanism can be specified on the relationship by passing `sa_relationship_kwargs={"lazy": method}` to `sqlmodel.Relationship`, which will use the given loading mechanism in all queries by default. ```python from typing import List, Optional import sqlmodel import reflex as rx class Post(rx.Model, table=True): ... user: Optional["User"] = sqlmodel.Relationship( back_populates="posts", sa_relationship_kwargs={"lazy": "selectin"}, ) flags: Optional[List["Flag"]] = sqlmodel.Relationship( back_populates="post", sa_relationship_kwargs={"lazy": "selectin"}, ) ``` ### Querying Related Rows on Demand For master-detail interfaces — a table of parent rows where selecting a row reveals its related rows — it is not necessary to eager load every relationship up front. Store the selected id in state and query only the related rows for that selection, filtering on the foreign key. ```python from typing import List, Optional import reflex as rx from sqlmodel import select class PostFlagsState(rx.State): posts: List[Post] = [] selected_post_id: Optional[int] = None selected_flags: List[Flag] = [] @rx.event def load_posts(self): with rx.session() as session: self.posts = session.exec(select(Post).limit(15)).all() @rx.event def select_post(self, post_id: int): self.selected_post_id = post_id with rx.session() as session: self.selected_flags = session.exec( select(Flag).where(Flag.post_id == post_id) ).all() ``` This runs a single indexed query per selection and avoids holding every child row in state — the `index=True` on `Flag.post_id` keeps the foreign-key lookup off a full table scan (adding the index to an existing table requires a migration). Avoid the inverse pattern of querying children in a Python loop over parents (the "N+1" pattern) — when the children of many parents are needed at once, use one query with `.in_()` on the foreign key, or one of the eager loading options above. ```python post_ids = [post.id for post in self.posts] with rx.session() as session: flags = session.exec(select(Flag).where(Flag.post_id.in_(post_ids))).all() ``` # Tables Source: http://localhost:3000/docs/database/tables.md Tables are database objects that contain all the data in a database. In tables, data is logically organized in a row-and-column format similar to a spreadsheet. Each row represents a unique record, and each column represents a field in the record. ## Creating a Table To create a table, make a class that inherits from `rx.Model`. The following example shows how to create a table called `User`. ```python class User(rx.Model, table=True): username: str email: str ``` The `table=True` argument tells Reflex to create a table in the database for this class. ### Primary Key By default, Reflex will create a primary key column called `id` for each table. However, if an `rx.Model` defines a different field with `primary_key=True`, then the default `id` field will not be created. A table may also redefine `id` as needed. It is not currently possible to create a table without a primary key. ## Advanced Column Types SQLModel automatically maps basic python types to SQLAlchemy column types, but for more advanced use cases, it is possible to define the column type using `sqlalchemy` directly. For example, we can add a last updated timestamp to the post example as a proper `DateTime` field with timezone. ```python import datetime import sqlmodel import sqlalchemy class Post(rx.Model, table=True): ... update_ts: datetime.datetime = sqlmodel.Field( default=None, sa_column=sqlalchemy.Column( "update_ts", sqlalchemy.DateTime(timezone=True), server_default=sqlalchemy.func.now(), ), ) ``` To make the `Post` model more usable on the frontend, a `dict` method may be provided that converts any fields to a JSON serializable value. In this case, the dict method is overriding the default `datetime` serializer to strip off the microsecond part. ```python class Post(rx.Model, table=True): ... def dict(self, *args, **kwargs) -> dict: d = super().dict(*args, **kwargs) d["update_ts"] = self.update_ts.replace(microsecond=0).isoformat() return d ``` # AG Chart Source: http://localhost:3000/docs/enterprise/ag-chart.md AG Chart is a powerful charting library that provides interactive charts and data visualization components for enterprise applications. ```python demo exec import reflex as rx import reflex_enterprise as rxe def basic_chart(): return rxe.ag_chart( options={ "data": [ {"month": "Jan", "value": 10}, {"month": "Feb", "value": 20}, {"month": "Mar", "value": 15}, ], "series": [ { "type": "line", "xKey": "month", "yKey": "value", } ], }, width="100%", height="400px", ) ``` For more detailed documentation, see the [AG Chart Documentation](https://charts.ag-grid.com/). # AG Grid Aligned Grids in Python Source: http://localhost:3000/docs/enterprise/ag-grid/aligned-grids.md --- meta_description: "Build aligned grids with AG Grid in Reflex. Synchronize columns and horizontal scrolling across multiple data grids in Python, no JavaScript required." title: Aligned Grids --- AgGrid provides a way to align multiple grids together. This is useful when you want to display related data in a synchronized manner. You can do so through the `aligned_grids` prop. This prop takes a list of grid IDs that you want to align. ```python demo exec import pandas as pd import reflex as rx import reflex_enterprise as rxe # Olympic winners data (originally from https://www.ag-grid.com/example-assets/olympic-winners.json) df = pd.read_json("data/olympic-winners.json") row_data = df.to_dict("records") column_defs = [ {"field": "athlete"}, {"field": "age"}, {"field": "country"}, {"field": "year"}, {"field": "sport"}, { "header_name": "Medals", "children": [ { "field": "total", "column_group_show": "closed", "col_id": "total", "value_getter": "params.data.gold + params.data.silver + params.data.bronze", "width": 100, }, {"field": "gold", "column_group_show": "open", "width": 100}, {"field": "silver", "column_group_show": "open", "width": 100}, {"field": "bronze", "column_group_show": "open", "width": 100}, ], }, ] def aligned_grids_page(): """Aligned grids demo.""" return rx.el.div( rxe.ag_grid( id="grid1", column_defs=column_defs, row_data=row_data, aligned_grids=["grid2"], width="100%", ), rxe.ag_grid( id="grid2", column_defs=column_defs, row_data=row_data, aligned_grids=["grid1"], width="100%", ), class_name="flex flex-col gap-y-6 w-full", ) ``` ```md alert warning # The pivot functionality does not work with aligned grids. This is because pivoting data changes the columns, which would make the aligned grids incompatible, as they are no longer sharing the same set of columns. ``` # AG Grid Cell Selection in Python Source: http://localhost:3000/docs/enterprise/ag-grid/cell-selection.md --- meta_description: "Configure cell and range selection in AG Grid with Reflex. Track selected cells and ranges in your Python data grid, all in pure Python, no JavaScript required." title: "Cell Selection" order: 8 --- AG Grid provides powerful cell selection capabilities that allow users to select individual cells or ranges of cells. This feature is essential for data manipulation, copying, and advanced interactions like fill handle operations. ## Range Selection To enable cell selection in your AG Grid, set the `cell_selection` prop to `True`. This automatically enables both single cell selection and range selection capabilities. ### Basic Selection Example ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd class CellSelectionState(rx.State): data: list[dict] = [] @rx.event def load_data(self): df = pd.read_json("https://www.ag-grid.com/example-assets/olympic-winners.json") self.data = df.head(10).to_dict("records") @rx.event def echo_selection(self, ranges: list[dict], started: bool, finished: bool): if finished: yield rx.toast(f"Selected ranges: {ranges}") column_defs = [ {"field": "athlete", "width": 150}, {"field": "age", "width": 90}, {"field": "country", "width": 120}, {"field": "year", "width": 90}, {"field": "sport", "width": 120}, {"field": "gold", "width": 100}, {"field": "silver", "width": 100}, {"field": "bronze", "width": 100}, ] def basic_cell_selection(): return rx.vstack( rx.text( "Click and drag to select cells. Selection info will appear in a toast.", size="2", ), rxe.ag_grid( id="basic_cell_selection_grid", column_defs=column_defs, row_data=CellSelectionState.data, cell_selection=True, on_cell_selection_changed=CellSelectionState.echo_selection, width="100%", height="400px", ), on_mount=CellSelectionState.load_data, width="100%", ) ``` ### Advanced Selection Event Handling For more sophisticated selection handling, you can process the selection ranges to calculate detailed information: ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd class AdvancedSelectionState(rx.State): data: list[dict] = [] @rx.event def load_data(self): df = pd.DataFrame({ "name": ["Alice", "Bob", "Charlie", "Diana", "Eve"], "score": [85, 92, 78, 96, 88], "grade": ["B", "A", "C", "A", "B"], "attempts": [3, 2, 4, 1, 3], }) self.data = df.to_dict("records") @rx.event def handle_selection(self, ranges: list[dict], started: bool, finished: bool): if finished and ranges: total_cells = sum( (r.get("endRow", 0) - r.get("startRow", 0) + 1) * len(r.get("columns", [])) for r in ranges ) yield rx.toast(f"Selected {total_cells} cells across {len(ranges)} ranges") editable_column_defs = [ {"field": "name", "width": 120}, {"field": "score", "width": 100, "editable": True}, {"field": "grade", "width": 100, "editable": True}, {"field": "attempts", "width": 120, "editable": True}, ] def advanced_selection_example(): return rx.vstack( rx.text( "Select ranges of cells. Try selecting multiple ranges by holding Ctrl/Cmd.", size="2", ), rxe.ag_grid( id="advanced_selection_grid", column_defs=editable_column_defs, row_data=AdvancedSelectionState.data, cell_selection=True, on_cell_selection_changed=AdvancedSelectionState.handle_selection, width="100%", height="300px", ), on_mount=AdvancedSelectionState.load_data, width="100%", ) ``` ## Fill Handle The fill handle is a powerful feature that allows users to quickly fill cells by dragging from a selected cell or range. When enabled, a small square appears at the bottom-right corner of the selection that users can drag to fill adjacent cells. ### Enabling Fill Handle To enable the fill handle, configure the `cell_selection` prop with a dictionary containing the handle configuration: ```python cell_selection = { "mode": "multiCell", # or "singleCell" to restrict selection to one cell "handle": { "mode": "fill", # Enable fill handle "direction": "xy", # "x" (horizontal), "y" (vertical), or "xy" (both) }, } ``` The fill handle is configured entirely through `cell_selection` — props like `enable_fill_handle`, `fill_handle`, or `grid_options={"enableFillHandle": True}` do not exist. Similarly, range selection is enabled with `cell_selection=True`, not `enable_range_selection`. To exclude specific columns from fill operations (typically text columns where an incremental series makes no sense), set `suppress_fill_handle: True` on the column definition: ```python column_defs = [ {"field": "athlete", "suppress_fill_handle": True}, {"field": "age", "editable": True, "type": "numericColumn"}, ] ``` Fill behavior depends on the data type: numbers extend as an incremental series, while text and dates are copied to the filled cells. ### Fill Handle Events When using the fill handle, it will trigger `on_cell_value_changed` for each cell receiving a fill value. This allows your backend to handle the data changes appropriately. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd class FillHandleState(rx.State): data: list[dict] = [] change_log: list[str] = [] @rx.event def load_data(self): df = pd.DataFrame({ "item": ["Apple", "Banana", "Cherry", "Date", "Elderberry"], "quantity": [10, 15, 8, 12, 20], "price": [1.50, 0.75, 2.00, 3.00, 4.50], "total": [15.00, 11.25, 16.00, 36.00, 90.00], }) self.data = df.to_dict("records") @rx.event def handle_cell_change(self, data: dict): row_index = data.get("rowIndex", 0) field = data.get("colId", "") new_value = data.get("newValue", "") old_value = data.get("oldValue", "") change_msg = f"Row {row_index + 1}, {field}: '{old_value}' → '{new_value}'" self.change_log = [change_msg] + self.change_log[:9] # Keep last 10 changes # Update the data if 0 <= row_index < len(self.data): self.data[row_index][field] = new_value fill_column_defs = [ {"field": "item", "width": 120}, {"field": "quantity", "width": 100, "editable": True, "type": "numericColumn"}, {"field": "price", "width": 100, "editable": True, "type": "numericColumn"}, {"field": "total", "width": 100, "editable": True, "type": "numericColumn"}, ] def fill_handle_example(): return rx.vstack( rx.text( "Select a cell and drag the fill handle (small square at bottom-right) to fill adjacent cells.", size="2", ), rxe.ag_grid( id="fill_handle_grid", column_defs=fill_column_defs, row_data=FillHandleState.data, cell_selection={ "handle": { "mode": "fill", # Enable fill handle } }, on_cell_value_changed=FillHandleState.handle_cell_change, width="100%", height="300px", ), rx.divider(), rx.text("Recent Changes:", weight="bold", size="3"), rx.cond( FillHandleState.change_log, rx.vstack( rx.foreach( FillHandleState.change_log, lambda change: rx.text(change, size="1", color="gray"), ), spacing="1", ), rx.text("No changes yet", size="2", color="gray"), ), on_mount=FillHandleState.load_data, width="100%", spacing="4", ) ``` ## Advanced Configuration Options You can further customize cell selection behavior using additional configuration options: ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd class ConfigurationState(rx.State): data: list[dict] = [] @rx.event def load_data(self): df = pd.DataFrame({ "id": range(1, 8), "name": [ "Product A", "Product B", "Product C", "Product D", "Product E", "Product F", "Product G", ], "category": [ "Electronics", "Clothing", "Electronics", "Books", "Clothing", "Electronics", "Books", ], "price": [299.99, 49.99, 199.99, 24.99, 79.99, 399.99, 19.99], "stock": [15, 32, 8, 45, 23, 12, 67], }) self.data = df.to_dict("records") configuration_column_defs = [ {"field": "id", "width": 80}, {"field": "name", "width": 150, "editable": True}, {"field": "category", "width": 120}, {"field": "price", "width": 100, "editable": True, "type": "numericColumn"}, {"field": "stock", "width": 100, "editable": True, "type": "numericColumn"}, ] def configuration_example(): return rx.vstack( rx.text("Cell selection with additional configuration options", size="2"), rxe.ag_grid( id="configuration_grid", column_defs=configuration_column_defs, row_data=ConfigurationState.data, cell_selection={ "handle": { "mode": "fill", } }, enable_cell_text_selection=True, # Allow text selection within cells suppress_cell_focus=False, # Allow cell focus width="100%", height="350px", ), on_mount=ConfigurationState.load_data, width="100%", ) ``` ## Key Features - **Cell Selection**: Enable with `cell_selection=True` for both single cell and range selection capabilities - **Fill Handle**: Configure with `cell_selection={"handle": {"mode": "fill"}}` for drag-to-fill functionality - **Event Handling**: Use `on_cell_selection_changed` to respond to selection changes - **Value Changes**: Use `on_cell_value_changed` to handle individual cell edits and fill operations - **Text Selection**: Enable `enable_cell_text_selection=True` to allow text selection within cells ## Best Practices 1. **Use cell_selection configuration**: Both single cell and range selection are automatically enabled with `cell_selection=True`, providing all necessary selection capabilities for fill operations. 2. **Handle cell value changes**: When using fill handle, implement `on_cell_value_changed` to process the data updates in your backend. 3. **Provide user feedback**: Use toasts or other UI elements to confirm selection actions and data changes. 4. **Consider performance**: For large datasets, be mindful of the performance impact of frequent cell value change events. 5. **Validate fill operations**: Implement validation logic in your `on_cell_value_changed` handler to ensure data integrity. # AG Grid Column Definitions in Python Source: http://localhost:3000/docs/enterprise/ag-grid/column-defs.md --- meta_description: "Define AG Grid columns in Python with Reflex. Configure column definitions — field, header, type, sorting, filtering, and width — all in pure Python." order: 1 --- ## Basic Columns AgGrid allows you to define the columns of your grid, passed to the prop `column_defs`. Each dictionary represents a column. ```md alert warning # If you are converting from other AG Grid implementation, we also support camelCase for the name of the properties. ``` Here we define a grid with 3 columns: ```python column_defs = [ {"field": "direction"}, {"field": "strength"}, {"field": "frequency"}, ] ``` To set default properties for all your columns, you can define `default_col_def` in your grid: ```python default_col_def = { "sortable": True, "filter": True, "resizable": True, } ``` # AG Grid in Python: Interactive Data Grid Source: http://localhost:3000/docs/enterprise/ag-grid.md --- title: "AgGrid Overview" order: 3 meta_description: "Use AG Grid in Python with Reflex. Build interactive, enterprise-grade data grids with sorting, filtering, pagination, and pivoting — all in pure Python." --- AG Grid (also written **ag-grid** or **aggrid**) is a powerful, feature-rich React data grid, and Reflex lets you use it entirely in Python — no JavaScript required. It brings enterprise-grade table functionality to your Reflex applications: with support for sorting, filtering, pagination, row selection, pivoting, and much more, AG Grid transforms how you display and interact with tabular data, whether it comes from a pandas DataFrame or your app's state. [Explore the full AG Grid showcase and examples](https://aggrid.reflex.run/) ## Your First Reflex AG Grid A basic Reflex AG Grid contains column definitions `column_defs`, which define the columns to be displayed in the grid, and `row_data`, which contains the data to be displayed in the grid. Each grid also requires a unique `id`, which is needed to uniquely identify the Ag-Grid instance on the page. If you have multiple grids on the same page, each grid must have a unique `id` so that it can be correctly rendered and managed. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/wind_dataset.csv") column_defs = [ {"field": "direction"}, {"field": "strength"}, {"field": "frequency"}, ] def ag_grid_simple(): return rxe.ag_grid( id="ag_grid_basic_1", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", ) ``` 📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) The format of the data passed to the `row_data` prop is a list of dictionaries. Each dictionary represents a row in the grid as seen below. ```python [ {"direction": "N", "strength": "0-1", "frequency": 0.5}, {"direction": "NNE", "strength": "0-1", "frequency": 0.6}, {"direction": "NE", "strength": "0-1", "frequency": 0.5}, ] ``` The previous example showed the `column_defs` written out in full. You can also extract the required information from the dataframe's column names: ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/wind_dataset.csv") def ag_grid_simple_2(): return rxe.ag_grid( id="ag_grid_basic_2", row_data=df.to_dict("records"), column_defs=[{"field": i} for i in df.columns], width="100%", height="40vh", ) ``` 📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) ## Headers In the above example, the first letter of the field names provided are capitalized when displaying the header name. You can customize the header names by providing a `header_name` key in the column definition. In this example, the `header_name` is customized for the second and third columns. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country"}, {"field": "pop", "headerName": "Population"}, {"field": "lifeExp", "headerName": "Life Expectancy"}, ] def ag_grid_simple_headers(): return rxe.ag_grid( id="ag_grid_basic_headers", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ## Column Filtering Allow a user to filter a column by setting the `filter` key to `True` in the column definition. In this example we enable filtering for the first and last columns. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "headerName": "Country", "filter": True}, {"field": "pop", "headerName": "Population"}, {"field": "lifeExp", "headerName": "Life Expectancy", "filter": True}, ] def ag_grid_simple_column_filtering(): return rxe.ag_grid( id="ag_grid_basic_column_filtering", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ) ``` ### Filter Types You can set `filter=True` to enable the default filter for a column. You can also set the filter type using the `filter` key. The following filter types are available: `ag_grid.filters.date`, `ag_grid.filters.number` and `ag_grid.filters.text`. These ensure that the input you enter to the filter is of the correct type. (`ag_grid.filters.set` and `ag_grid.filters.multi` are available with AG Grid Enterprise) ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/GanttChart-updated.csv") column_defs = [ {"field": "Task", "filter": True}, {"field": "Start", "filter": rxe.ag_grid.filters.date}, {"field": "Duration", "filter": rxe.ag_grid.filters.number}, {"field": "Resource", "filter": rxe.ag_grid.filters.text}, ] def ag_grid_column_filter_types(): return rxe.ag_grid( id="ag_grid_basic_column_filtering", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ) ``` 📊 **Dataset source:** [GanttChart-updated.csv](https://raw.githubusercontent.com/plotly/datasets/master/GanttChart-updated.csv) To show an inline filter input below the column headers, set `floating_filter: True` in the column definition (or in `default_col_def` to apply it to every column). ### Multi-Column Filter (Enterprise) The enterprise `agMultiColumnFilter` combines several filter types on a single column: ```python column_defs = [ { "field": "athlete", "filter": "agMultiColumnFilter", "filter_params": { "filters": [ {"filter": "agTextColumnFilter"}, {"filter": "agSetColumnFilter"}, ], }, }, ] ``` Enterprise filters may require loading their modules explicitly via the `enterprise_modules` prop — for example `SetFilterModule` for `agSetColumnFilter`, `MultiFilterModule` for `agMultiColumnFilter`, and `FiltersToolPanelModule` for the filter tool panel (shown with `side_bar=True`). See [Functionality you need is not available/working in Reflex](#functionality-you-need-is-not-availableworking-in-reflex) below. ## Row Sorting By default, the rows can be sorted by any column by clicking on the column header. You can disable sorting of the rows for a column by setting the `sortable` key to `False` in the column definition. In this example, we disable sorting for the first column. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "sortable": False}, {"field": "pop", "headerName": "Population"}, {"field": "lifeExp", "headerName": "Life Expectancy"}, ] def ag_grid_simple_row_sorting(): return rxe.ag_grid( id="ag_grid_basic_row_sorting", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ## Row Selection Row Selection is enabled using the `row_selection` attribute. Setting it to `multiple` allows users to select multiple rows at a time. You can use the `checkbox_selection` column definition attribute to render checkboxes for selection. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "checkboxSelection": True}, {"field": "pop", "headerName": "Population"}, {"field": "continent"}, ] def ag_grid_simple_row_selection(): return rxe.ag_grid( id="ag_grid_basic_row_selection", row_data=df.to_dict("records"), column_defs=column_defs, row_selection={"mode": "multiple"}, width="100%", height="40vh", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ### Handling Selection Changes Use the `on_selection_changed` event trigger to react to selection changes. The event handler receives the selected rows directly as a `list[dict]` — not an event object, so don't try to read the rows from `event["rows"]` or through the grid API: ```python class GridSelectionState(rx.State): selected_rows: list[dict] = [] @rx.event def handle_selection_changed(self, selected_rows: list[dict]): self.selected_rows = selected_rows return rx.toast(f"Selected {len(selected_rows)} rows") def grid_with_selection(): return rxe.ag_grid( id="selection_grid", row_data=df.to_dict("records"), column_defs=column_defs, row_selection={"mode": "multiRow"}, on_selection_changed=GridSelectionState.handle_selection_changed, width="100%", height="40vh", ) ``` An event handler annotated as `def handle(self, event: dict)` raises an `EventHandlerArgTypeMismatchError`, since the trigger passes a `list[dict]`. ## Editing Enable Editing by setting the `editable` attribute to `True`. The cell editor is inferred from the cell data type. Set the cell editor type using the `cell_editor` attribute. There are 7 provided cell editors in AG Grid: 1. `ag_grid.editors.text` 2. `ag_grid.editors.large_text` 3. `ag_grid.editors.select` 4. `ag_grid.editors.rich_select` 5. `ag_grid.editors.number` 6. `ag_grid.editors.date` 7. `ag_grid.editors.checkbox` In this example, we enable editing for the second and third columns. The second column uses the `number` cell editor, and the third column uses the `select` cell editor. The `on_cell_value_changed` event trigger is linked to the `cell_value_changed` event handler in the state. This event handler is called whenever a cell value is changed and changes the value of the backend var `_data_df` and the state var `data`. ```python import reflex as rx import reflex_enterprise as rxe import pandas as pd class AGGridEditingState(rx.State): data: list[dict] = [] _data_df: pd.DataFrame @rx.event def load_data(self): self._data_df = pd.read_csv("data/gapminder2007.csv") self.data = self._data_df.to_dict("records") @rx.event def cell_value_changed(self, row, col_field, new_value): self._data_df.at[row, col_field] = new_value self.data = self._data_df.to_dict("records") yield rx.toast( f"Cell value changed, Row: {row}, Column: {col_field}, New Value: {new_value}" ) column_defs = [ {"field": "country"}, { "field": "pop", "headerName": "Population", "editable": True, "cellEditor": rxe.ag_grid.editors.number, }, { "field": "continent", "editable": True, "cellEditor": rxe.ag_grid.editors.select, "cellEditorParams": { "values": ["Asia", "Europe", "Africa", "Americas", "Oceania"] }, }, ] def ag_grid_simple_editing(): return rxe.ag_grid( id="ag_grid_basic_editing", row_data=AGGridEditingState.data, column_defs=column_defs, on_cell_value_changed=AGGridEditingState.cell_value_changed, on_mount=AGGridEditingState.load_data, width="100%", height="40vh", ) ``` ## Pagination By default, the grid uses a vertical scroll. You can reduce the amount of scrolling required by adding pagination. To add pagination, set `pagination=True`. You can set the `pagination_page_size` to the number of rows per page and `pagination_page_size_selector` to a list of options for the user to select from. ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country"}, {"field": "pop", "headerName": "Population"}, {"field": "lifeExp", "headerName": "Life Expectancy"}, ] def ag_grid_simple_pagination(): return rxe.ag_grid( id="ag_grid_basic_pagination", row_data=df.to_dict("records"), column_defs=column_defs, pagination=True, pagination_page_size=10, pagination_page_size_selector=[10, 40, 100], width="100%", height="40vh", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ## AG Grid with State ### Putting Data in State Assuming you want to make any edit to your data, you can put the data in State. This allows you to update the grid based on user input. Whenever the `data` var is updated, the grid will be re-rendered with the new data. ```python from typing import Any import reflex as rx import reflex_enterprise as rxe import pandas as pd class AGGridState2(rx.State): data: list[dict] = [] @rx.event def load_data(self): _df = pd.read_csv("data/gapminder2007.csv") self.data = _df.to_dict("records") column_defs = [ {"field": "country"}, {"field": "pop", "headerName": "Population"}, {"field": "continent"}, ] def ag_grid_state_2(): return rxe.ag_grid( id="ag_grid_state_2", row_data=AGGridState2.data, column_defs=column_defs, on_mount=AGGridState2.load_data, width="100%", height="40vh", ) ``` ### Updating the Grid with State You can use State to update the grid based on a users input. In this example, we update the `column_defs` of the grid when a user clicks a button. ```python import reflex as rx import reflex_enterprise as rxe import pandas as pd class AgGridState(rx.State): """The app state.""" all_columns: list = [] two_columns: list = [] column_defs: list = all_columns n_clicks = 0 @rx.event def init_columns(self): self.all_columns = [ {"field": "country"}, {"field": "pop"}, {"field": "continent"}, {"field": "lifeExp"}, {"field": "gdpPercap"}, ] self.two_columns = [ {"field": "country"}, {"field": "pop"}, ] self.column_defs = self.all_columns @rx.event def update_columns(self): self.n_clicks += 1 if self.n_clicks % 2 != 0: self.column_defs = self.two_columns else: self.column_defs = self.all_columns df = pd.read_csv("data/gapminder2007.csv") def ag_grid_simple_with_state(): return rx.box( rx.button("Toggle Columns", on_click=AgGridState.update_columns), rxe.ag_grid( id="ag_grid_basic_with_state", row_data=df.to_dict("records"), column_defs=AgGridState.column_defs, on_mount=AgGridState.init_columns, width="100%", height="40vh", ), width="100%", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ## AG Grid with Data from a Database In this example, we will use a database to store the data. The data is loaded from a csv file and inserted into the database when the page is loaded using the `insert_dataframe_to_db` event handler. The data is then fetched from the database and displayed in the grid using the `data` computed var. When a cell value is changed, the data is updated in the database using the `cell_value_changed` event handler. ```python import reflex as rx import reflex_enterprise as rxe import pandas as pd from sqlmodel import select class Country(rx.Model, table=True): country: str population: int continent: str class AGGridDatabaseState(rx.State): countries: list[Country] # Insert data from a csv loaded dataframe to the database (Do this on the page load) @rx.event def insert_dataframe_to_db(self): data = pd.read_csv("data/gapminder2007.csv") with rx.session() as session: for _, row in data.iterrows(): db_record = Country( country=row["country"], population=row["pop"], continent=row["continent"], ) session.add(db_record) session.commit() # Fetch data from the database using a computed variable @rx.var def data(self) -> list[dict]: with rx.session() as session: results = session.exec(select(Country)).all() self.countries = [result.dict() for result in results] return self.countries # Update the database when a cell value is changed @rx.event def cell_value_changed(self, row, col_field, new_value): self.countries[row][col_field] = new_value with rx.session() as session: country = Country(**self.countries[row]) session.merge(country) session.commit() yield rx.toast( f"Cell value changed, Row: {row}, Column: {col_field}, New Value: {new_value}" ) column_defs = [ {"field": "country"}, { "field": "population", "headerName": "Population", "editable": True, "cellEditor": rxe.ag_grid.editors.number, }, { "field": "continent", "editable": True, "cellEditor": rxe.ag_grid.editors.select, "cellEditorParams": { "values": ["Asia", "Europe", "Africa", "Americas", "Oceania"] }, }, ] def index(): return rxe.ag_grid( id="ag_grid_basic_editing", row_data=AGGridDatabaseState.data, column_defs=column_defs, on_cell_value_changed=AGGridDatabaseState.cell_value_changed, width="100%", height="40vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, on_load=AGGridDatabaseState.insert_dataframe_to_db) ``` ## Using AG Grid Enterprise AG Grid offers both community and enterprise versions. See the [AG Grid docs](https://www.ag-grid.com/archive/31.2.0/react-data-grid/licensing/) for details on purchasing a license key. To use an AG Grid Enterprise license key with Reflex AG Grid set the environment variable `AG_GRID_LICENSE_KEY`: ```bash export AG_GRID_LICENSE_KEY="your_license_key" ``` ## column_def props The following props are available for `column_defs` as well as many others that can be found here: [AG Grid Column Def Docs](https://www.ag-grid.com/react-data-grid/column-properties/). (it is necessary to use snake_case for the keys in Reflex, unlike in the AG Grid docs where camelCase is used) - `field`: `str`: The field of the row object to get the cell's data from. - `col_id`: `str | None`: The unique ID to give the column. This is optional. If missing, the ID will default to the field. - `type`: `str | None`: The type of the column. - `cell_data_type`: `bool | str | None`: The data type of the cell values for this column. Can either infer the data type from the row data (true - the default behaviour), define a specific data type (string), or have no data type (false). - `hide`: `bool`: Set to true for this column to be hidden. - `editable`: `bool | None`: Set to true if this column is editable, otherwise false. - `filter`: `AGFilters | str | None`: Filter component to use for this column. Set to true to use the default filter. Set to the name of a provided filter to use that filter. (Check out the Filter Types section of this page for more information) - `floating_filter`: `bool`: Whether to display a floating filter for this column. - `header_name`: `str | None`: The name to render in the column header. If not specified and field is specified, the field name will be used as the header name. - `header_tooltip`: `str | None`: Tooltip for the column header. - `checkbox_selection`: `bool | None`: Set to true to render a checkbox for row selection. - `cell_editor`: `AGEditors | str | None`: Provide your own cell editor component for this column's cells. (Check out the Editing section of this page for more information) - `cell_editor_params`: `dict[str, list[Any]] | None`: Params to be passed to the cellEditor component. ## Functionality you need is not available/working in Reflex All AGGrid options found in this [documentation](https://www.ag-grid.com/react-data-grid/reference/) are mapped in rxe.ag_grid, but some features might not have been fully tested, due to the sheer number of existing features in the underlying AG Grid library. If one of the ag_grid props does not import the expected module, you can pass it manually via the props `community_modules` or `enterprise_modules`, which expect a `set[str]` of the module names. You will get a warning in the browser console if a module is missing, so you can check there if a feature is not working as expected. You can also report the missing module on our discord or GitHub issues page of the main Reflex repository. Best practice is to create a single instance of `ag_grid.api()` with the same `id` as the `id` of the `ag_grid` component that is to be referenced, `"ag_grid_basic_row_selection"` in this first example. The example below uses the `select_all()` and `deselect_all()` methods of the AG Grid API to select and deselect all rows in the grid. This method is not available in Reflex directly. Check out this [documentation](https://www.ag-grid.com/react-data-grid/grid-api/#reference-selection-selectAll) to see what the methods look like in the AG Grid docs. ```md alert info # Ensure that the docs are set to React tab in AG Grid ``` ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "checkboxSelection": True}, {"field": "pop"}, {"field": "continent"}, ] def ag_grid_api_simple(): my_api = rxe.ag_grid.api(id="ag_grid_basic_row_selection") return rx.vstack( rxe.ag_grid( id="ag_grid_basic_row_selection", row_data=df.to_dict("records"), column_defs=column_defs, row_selection="single", width="100%", height="40vh", ), rx.button("Select All", on_click=my_api.select_all()), rx.button("Deselect All", on_click=my_api.deselect_all()), spacing="4", width="100%", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) The react code for the `select_all()` event handler is `selectAll = (source?: SelectionEventSourceType) => void;`. To use this in Reflex as you can see, it should be called in snake case rather than camel case. The `void` means it doesn't return anything. The `source?` indicates that it takes an optional `source` argument. ```md alert info # Another way to use the AG Grid API It is also possible to use the AG Grid API directly with the event trigger (`on_click`) of the component. This removes the need to create a variable `my_api`. This is shown in the example below. It is necessary to use the `id` of the `ag_grid` component that is to be referenced. ```python rx.button("Select all", on_click=rxe.ag_grid.api(id="ag_grid_basic_row_selection").select_all()), ``` ``` ### More examples The following example lets a user [export the data as a csv](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-export-exportDataAsCsv) and [adjust the size of columns to fit the available horizontal space](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-columnSizing-sizeColumnsToFit). (Try resizing the screen and then clicking the resize columns button) ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "checkboxSelection": True}, {"field": "pop"}, {"field": "continent"}, ] def ag_grid_api_simple2(): my_api = rxe.ag_grid.api(id="ag_grid_export_and_resize") return rx.vstack( rxe.ag_grid( id="ag_grid_export_and_resize", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ), rx.button("Export", on_click=my_api.export_data_as_csv()), rx.button("Resize Columns", on_click=my_api.size_columns_to_fit()), spacing="4", width="100%", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) The react code for both of these is shown below. The key point to see is that both of these functions return `void` and therefore does not return anything. `exportDataAsCsv = (params?: CsvExportParams) => void;` `sizeColumnsToFit = (paramsOrGridWidth?: ISizeColumnsToFitParams | number) => void;` ### Example with a Return Value This example shows how to get the data from `ag_grid` as a [csv on the backend](https://www.ag-grid.com/javascript-data-grid/grid-api/#reference-export-getDataAsCsv). The data that was passed to the backend is then displayed as a toast with the data. ```python import reflex as rx import reflex_enterprise as rxe import pandas as pd class AGGridStateAPI(rx.State): def handle_get_data(self, data: str): yield rx.toast(f"Got CSV data: {data}") df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country", "checkboxSelection": True}, {"field": "pop"}, {"field": "continent"}, ] def ag_grid_api_argument(): my_api = rxe.ag_grid.api(id="ag_grid_get_data_as_csv") return rx.vstack( rxe.ag_grid( id="ag_grid_get_data_as_csv", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", height="40vh", ), rx.button( "Get CSV data on backend", on_click=my_api.get_data_as_csv(callback=AGGridStateAPI.handle_get_data), ), spacing="4", width="100%", ) ``` The react code for the `get_data_as_csv` method of the AG Grid API is `getDataAsCsv = (params?: CsvExportParams) => string | undefined;`. Here the function returns a `string` (or undefined). In Reflex to handle this returned value it is necessary to pass a `callback` as an argument to the `get_data_as_csv` method that will get the returned value. In this example the `handle_get_data` event handler is passed as the callback. This event handler will be called with the returned value from the `get_data_as_csv` method. # AG Grid Master Detail in Python Source: http://localhost:3000/docs/enterprise/ag-grid/master-detail.md --- meta_description: "Enable master-detail rows in AG Grid with Reflex. Expand rows to reveal a nested detail grid backed by per-row data, configured entirely in Python." title: Master Detail --- Master-detail lets rows expand to show detailed information in a nested grid. Each master row carries its detail rows as a nested list, and an expandable column reveals them. Three pieces are required: 1. `master_detail=True` on the grid. 2. A column with `"cell_renderer": "agGroupCellRenderer"`, which renders the expand/collapse arrows. 3. `detail_cell_renderer_params` describing the detail grid's columns and how to extract the detail rows from the master row. ```python demo exec import reflex as rx import reflex_enterprise as rxe class MasterDetailState(rx.State): master_data: list[dict] = [ { "id": 1, "name": "Product A", "category": "Electronics", "price": 299.99, "counts": [ # Detail rows for this master row {"count": 10, "value": "Stock Level"}, {"count": 5, "value": "Orders Today"}, {"count": 25, "value": "Total Sales"}, ], }, { "id": 2, "name": "Product B", "category": "Clothing", "price": 49.99, "counts": [ {"count": 50, "value": "Stock Level"}, {"count": 12, "value": "Orders Today"}, {"count": 78, "value": "Total Sales"}, ], }, ] column_defs = [ { "field": "id", "header_name": "ID", "width": 80, "cell_renderer": "agGroupCellRenderer", # Required for expand/collapse }, {"field": "name", "header_name": "Product Name", "width": 150}, {"field": "category", "header_name": "Category", "width": 120}, { "field": "price", "header_name": "Price", "width": 100, "value_formatter": "params.value ? '$' + params.value.toFixed(2) : ''", }, ] detail_cell_renderer_params = { "detail_grid_options": { "column_defs": [ {"field": "count", "header_name": "Count"}, {"field": "value", "header_name": "Description"}, ] }, "get_detail_row_data": lambda params: rx.vars.function.FunctionStringVar( "params.successCallback" ).call(params.data.counts), } def master_detail_grid(): return rxe.ag_grid( id="master_detail_grid", row_data=MasterDetailState.master_data, column_defs=column_defs, master_detail=True, detail_cell_renderer_params=detail_cell_renderer_params, width="100%", height="500px", ) ``` ## How Detail Rows Are Provided `get_detail_row_data` follows AG Grid's asynchronous convention: the grid passes a `params` object containing the master row's `data` and a `successCallback` to invoke with the detail rows. The lambda above calls `params.successCallback` with the nested `counts` list of the expanded row. The detail grid is a full AG Grid instance with its own `column_defs`, independent from the master grid's columns. ## Static vs Stateful Configuration `row_data` and `column_defs` are plain serializable data, so they can live in state and change at runtime: ```python class MasterDetailState(rx.State): master_data: list[dict] = [] # fetch/replace at runtime column_defs: list[dict] = [] ``` `detail_cell_renderer_params` is different because it holds a callback (`get_detail_row_data`). A callable cannot be stored in a state var: Reflex serializes state to the client as JSON, and a Python lambda (or `FunctionStringVar`) has no JSON representation, so syncing that state raises a serialization error at runtime. Keep the renderer params as a module-level object and pass it to the grid directly — it is compiled into the app once and does not need to change per request: ```python DETAIL_PARAMS = { "detail_grid_options": {"column_defs": [{"field": "count"}]}, "get_detail_row_data": lambda params: rx.vars.function.FunctionStringVar( "params.successCallback" ).call(params.data.counts), } ``` Reserve state vars for the serializable pieces (row data, column defs) and leave the callback-bearing renderer params at module level. # AG Grid with a Pandas DataFrame in Python Source: http://localhost:3000/docs/enterprise/ag-grid/model-wrapper.md --- meta_description: "Wire AG Grid to a pandas DataFrame or a Reflex database model in Python. The model wrapper renders and edits your data in an interactive data grid, all in pure Python." order: 6 --- A model wrapper is an utility used to wrap a database model and provide a consistent interface over it. It allows automatically adding new rows to the database, updating existing rows, and deleting rows. ## Default Model Wrapper You can use the basic functionality of the model wrapper by using the `rxe.model_wrapper` function. This function takes a database model and returns a wrapper object that can be used to interact with the model. ```python import reflex as rx import reflex_enterprise as rxe def index_page(): return rx.box( rxe.model_wrapper(model_class=MyModel, width="100%"), height="80vh", ) ``` By default the model_wrapper use the infinite rows model from AgGrid. As the user scrolls, the wrapper automatically loads windows of rows from the database instead of loading the whole table into memory. The cache size can be tuned with the `max_blocks_in_cache` and `cache_block_size` props. ```md alert warning # Always place the model wrapper in a container with a calculable height. If the containing element has no fixed height, the grid will not render. Setting e.g. `height="80vh"` on the parent box is enough. ``` The model passed as `model_class` must exactly match the schema of the database table backing it, or querying will fail. In particular, don't invent a primary key that the actual table doesn't have. ## Custom Model Wrapper If the default model wrapper does not fit your needs, you can create a custom model wrapper by subclassing the `rxe.ModelWrapper` class. This allows you to customize the behavior of the model wrapper to fit your specific use case. ```python import reflex_enterprise as rxe class MyCustomWrapper(rxe.ModelWrapper[MyModel]): pass ``` In the custom model wrapper, you can override the following methods: - `_get_columns_defs` - `_get_data` - `_row_count` - `on_value_setter` to modify how the model wrapper will behave. A custom wrapper is rendered with its `create` classmethod: ```python def index_page(): return rx.box( MyCustomWrapper.create(model_class=MyModel, width="100%"), height="80vh", ) ``` ### Authorization By default there is no authentication checking for any database operation performed through the grid — inserts, updates, and deletes are open to any user who can reach the page. To restrict operations, override the `_is_authorized` method in a `ModelWrapper` subclass: ```python from typing import Sequence import reflex_enterprise as rxe from reflex_enterprise.components.ag_grid.wrapper import ModelWrapperActionType class UserModelWrapper(rxe.ModelWrapper[User]): async def _is_authorized( self, action: ModelWrapperActionType, action_data: Sequence[User] | dict | None, ) -> bool: """Check if the user is authorized to perform the action. For SELECT, action_data is None. For INSERT, action_data is a dict of the new row data. For UPDATE, action_data is a dict of updated row data. For DELETE, action_data is a list of model objects to delete. """ auth_state = await self.get_state(AuthState) return auth_state.user_is_admin ``` ### Customizing Columns and Toolbar Override `_get_column_defs` to adjust the generated column definitions — for example to disable filtering or sorting on a specific field: ```python class UserModelWrapper(rxe.ModelWrapper[User]): def _get_column_defs(self): cols = super()._get_column_defs() for col in cols: if col.field == "internal_notes": col.filter = None col.sortable = False return cols ``` The toolbar UI can be customized as well: override the `_top_toolbar`, `_delete_button`, and `_add_dialog` classmethods to replace the default add/delete controls with your own components. ## SSRM Model Wrapper The SSRM model wrapper, used with `rxe.model_wrapper_ssrm`, is a version of the model wrapper that allows you to use the ServerSideRowModel of AgGrid. ```python import reflex as rx import reflex_enterprise as rxe def index_page(): return rx.box( rxe.model_wrapper_ssrm(model_class=MyModel, width="100%"), height="80vh", ) ``` ## SSRM Custom Model Wrapper In the same way you can extend the default model wrapper, you can extend the SSRM custom model wrapper by subclassing the `rxe.ModelWrapperSSRM` class. This allows you to customize the behavior of the model wrapper to fit your specific use case. ```python import reflex_enterprise as rxe class MyCustomSSRMWrapper(rxe.ModelWrapperSSRM[MyModel]): pass ``` The overridable methods are the same as the standard model wrapper. # AG Grid Pivot Mode in Python Source: http://localhost:3000/docs/enterprise/ag-grid/pivot-mode.md --- meta_description: "Enable pivot mode in AG Grid with Reflex. Build pivot tables in Python — group rows, pivot columns, and aggregate values in an interactive data grid, no JavaScript required." --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` Pivot mode allows you to visualize your data in a different way than how they are originally structured in the data source. When pivoting on a column, the values in that column will be used as column headers. This allows you to see the data in a more compact way, and can be useful when you have a lot of data to display. To enable pivot mode, set the `pivot_mode` property to `True` in the grid props. Once pivot mode is enabled, you can define which column to pivot on by setting the `pivot` property in a column definition. In addition to the pivot column, at least one column definition must have `row_group` property set to `True` to define the row grouping. You can also define how rows are aggregated by passing the `agg_func` property in the column definition. The `agg_func` property should be set to a string that represents the aggregation function to use. The built-in aggregation functions are `sum`, `min`, `max`, `count`, `avg`, `first`, and `last`. You can find a live example here: [Pivot Mode Example](https://aggrid.reflex.run/pivot). ```python demo exec import pandas as pd import reflex as rx import reflex_enterprise as rxe # Olympic winners data (originally from https://www.ag-grid.com/example-assets/olympic-winners.json) df = pd.read_json("data/olympic-winners.json") def pivot_page(): return ( rxe.ag_grid( id="sandbox_grid", column_defs=[ {"field": "country", "row_group": True}, {"field": "sport", "pivot": True}, {"field": "year", "pivot": True}, {"field": "gold", "aggFunc": "sum"}, ], loading=False, row_data=df.to_dict("records"), default_col_def={ "flex": 1, "min_width": 130, "enable_value": True, "enable_row_group": True, "enable_pivot": True, }, auto_group_column_def={ "minWidth": 200, "pinned": "left", }, pivot_mode=True, side_bar="columns", pivot_panel_show="always", width="100%", height="500px", ), ) ``` ## Pivot using State ```python demo exec import pandas as pd import reflex as rx import reflex_enterprise as rxe df = pd.read_csv("data/wind_dataset.csv") class PivotState(rx.State): """State for the sandbox page.""" pivot = False row_grouping = False @rx.event def toggle_pivot(self): """Toggle the pivot.""" self.pivot = not self.pivot @rx.event def toggle_row_grouping(self): """Toggle the row grouping.""" self.row_grouping = not self.row_grouping def sandbox_page(): """Sandbox page.""" return rx.vstack( rx.hstack( rx.text("Toggle Pivot"), rx.switch( on_click=PivotState.toggle_pivot, name="pivot", checked=PivotState.pivot, ), rx.text("Toggle Row Grouping"), rx.switch( on_click=PivotState.toggle_row_grouping, name="row_grouping", checked=PivotState.row_grouping, ), ), rxe.ag_grid( id="sandbox_grid", column_defs=[ rxe.ag_grid.column_def( field="direction", pivot=True, ), rxe.ag_grid.column_def( field="strength", ), rxe.ag_grid.column_def( field="frequency", agg_func="count", row_group=PivotState.row_grouping, ), ], row_data=df.to_dict("records"), pivot_mode=PivotState.pivot, pivot_panel_show="onlyWhenPivoting", width="100%", height="500px", ), width="100%", ) ``` 📊 **Dataset source:** [wind_dataset.csv](https://raw.githubusercontent.com/plotly/datasets/master/wind_dataset.csv) # AG Grid Themes in Python Source: http://localhost:3000/docs/enterprise/ag-grid/theme.md --- meta_description: "Theme and style AG Grid in Python with Reflex. Customize your data grid with built-in AG Grid themes and custom styling — all in pure Python, no JavaScript required." order: 3 --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` ```md alert warning # Only the old theme API of AG Grid is currently supported. The new theme API is not supported yet. ``` You can style your grid with a theme. AG Grid includes the following themes: 1. `quartz` 2. `alpine` 3. `balham` 4. `material` The grid uses `quartz` by default. To use any other theme, set it using the `theme` prop, i.e. `theme="alpine"`. ```python import reflex as rx import reflex_enterprise as rxe import pandas as pd class AGGridThemeState(rx.State): """The app state.""" theme: str = "quartz" themes: list[str] = ["quartz", "balham", "alpine", "material"] @rx.event def set_theme(self, value: str): self.theme = value df = pd.read_csv("data/gapminder2007.csv") column_defs = [ {"field": "country"}, {"field": "pop", "headerName": "Population"}, {"field": "lifeExp", "headerName": "Life Expectancy"}, ] def ag_grid_simple_themes(): return rx.vstack( rx.hstack( rx.text("Theme:"), rx.select( AGGridThemeState.themes, value=AGGridThemeState.theme, on_change=AGGridThemeState.set_theme, ), ), rxe.ag_grid( id="ag_grid_basic_themes", row_data=df.to_dict("records"), column_defs=column_defs, theme=AGGridThemeState.theme, width="100%", height="40vh", ), width="100%", ) ``` 📊 **Dataset source:** [gapminder2007.csv](https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv) ## Customizing a Theme with CSS The `theme` prop maps directly to one of AG Grid's built-in theme classes, which `reflex-enterprise` applies on the grid root element: - `theme="quartz"` → `.ag-theme-quartz` - `theme="balham"` → `.ag-theme-balham` - `theme="alpine"` → `.ag-theme-alpine` - `theme="material"` → `.ag-theme-material` Each theme also has a dark variant (`.ag-theme-quartz-dark`, `.ag-theme-balham-dark`, etc.). The dark variant is only applied when the corresponding theme name is selected; listing both light and dark class selectors in your CSS lets the same overrides apply in either mode. You can customize any theme by overriding the AG Grid CSS variables (the `--ag-*` custom properties) and by writing rules that target AG Grid's built-in classes. The full list of available variables is documented in the [AG Grid theming reference](https://www.ag-grid.com/javascript-data-grid/global-style-customisation-variables/). ### CSS Variables AG Grid exposes most visual properties as CSS variables. Commonly customized ones: - Typography: `--ag-font-family`, `--ag-font-size` - Density and shape: `--ag-grid-size` (base unit), `--ag-border-radius`, `--ag-wrapper-border-radius`, `--ag-cell-horizontal-padding` - Borders: `--ag-border-color`, `--ag-row-border-color` - Backgrounds: `--ag-background-color`, `--ag-odd-row-background-color`, `--ag-row-hover-color` - Headers: `--ag-header-background-color`, `--ag-header-foreground-color`, `--ag-header-column-separator-color` - Accents (selection / focus): `--ag-accent-color`, `--ag-selected-row-background-color`, `--ag-range-selection-background-color`, `--ag-input-focus-border-color`, `--ag-checkbox-checked-color` ### Scoping Overrides with a Wrapper Class If you have multiple grids on a page, scope your overrides to a parent class so they don't leak to other grids. Wrap the grid in an `rx.box` (or any element) with a class such as `custom-ag-grid`, then target the theme class _inside_ that wrapper in your CSS: ```python rx.box( rxe.ag_grid( id="my_themed_grid", row_data=df.to_dict("records"), column_defs=column_defs, theme="quartz", width="100%", height="40vh", ), class_name="custom-ag-grid", ) ``` ```css /* assets/ag_grid_theme.css */ /* * Custom ag-grid theme overrides. * * reflex-enterprise applies the built-in theme as a class on the grid root. * Scope overrides to a parent `.custom-ag-grid` container so they don't leak * to other grids on the page. */ .custom-ag-grid .ag-theme-quartz, .custom-ag-grid .ag-theme-quartz-dark, .custom-ag-grid .ag-theme-balham, .custom-ag-grid .ag-theme-balham-dark, .custom-ag-grid .ag-theme-alpine, .custom-ag-grid .ag-theme-alpine-dark, .custom-ag-grid .ag-theme-material, .custom-ag-grid .ag-theme-material-dark { --ag-font-family: "Inter", ui-sans-serif, system-ui, sans-serif; --ag-font-size: 13px; --ag-grid-size: 6px; --ag-border-radius: 12px; --ag-wrapper-border-radius: 12px; --ag-background-color: #ffffff; --ag-odd-row-background-color: #fafbfd; --ag-row-hover-color: #f1f5f9; --ag-header-background-color: #eef2ff; --ag-header-foreground-color: #1e293b; --ag-accent-color: #3b82f6; --ag-selected-row-background-color: #dbeafe; --ag-input-focus-border-color: #3b82f6; --ag-checkbox-checked-color: #3b82f6; --ag-cell-horizontal-padding: 16px; } /* Stronger header typography. */ .custom-ag-grid .ag-header-cell-text { font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; font-size: 11px; } ``` Listing every theme variant in the selector keeps the overrides applied no matter which built-in theme is selected at runtime, including its dark variant. ### Loading the Stylesheet Place your CSS file under `assets/` and add it to the `stylesheets` list on your `rxe.App` (or `rx.App`): ```python app = rxe.App( stylesheets=["/ag_grid_theme.css"], ) ``` The path is relative to `assets/`, so `assets/ag_grid_theme.css` is referenced as `/ag_grid_theme.css`. ### Overriding CSS Variables Inline via `style` For one-off tweaks you don't need a separate stylesheet — you can set `--ag-*` variables directly in the `style` dict on the grid: ```python rxe.ag_grid( id="comic_grid", row_data=df.to_dict("records"), column_defs=column_defs, theme="quartz", style={ "--ag-font-family": "Comic Sans MS !important", "--ag-font-size": "14px !important", }, width="100%", height="40vh", ) ``` ```md alert warning # CSS variable inheritance and `!important` Inline `style` sets the variable on the grid root element only. The built-in themes bind their styling to multiple CSS classes (`.ag-theme-quartz`, `.ag-theme-quartz .ag-cell`, etc.), and those rules often have higher specificity than a custom property defined inline. As a result, a value set via `style={"--ag-font-family": "..."}` may be ignored unless you append `!important`. If your inline override doesn't take effect, add `!important` or move the override into a stylesheet that targets the theme class with comparable specificity. Inline `style` overrides only apply to the grid they are set on. To customize multiple grids consistently, prefer a stylesheet scoped to a wrapper class (see above). ``` ## Per-Cell Custom Classes Built-in themes style the grid as a whole. To highlight individual cells based on their value, attach custom CSS classes via the `cellClass` and `cellClassRules` column-def keys, then style those classes in your stylesheet. - `cellClass` always applies the listed class(es) to every cell in the column. - `cellClassRules` is a mapping of `class name -> JS expression`; the class is applied when the expression evaluates truthy. The cell's value is available as `params.value`. ```python column_defs = [ {"field": "project"}, { "field": "status", "cellClass": "status-pill", "cellClassRules": { "status-active": "params.value === 'Active'", "status-blocked": "params.value === 'Blocked'", "status-complete": "params.value === 'Complete'", "status-planning": "params.value === 'Planning'", }, }, ] ``` ```css /* assets/ag_grid_theme.css */ .custom-ag-grid .status-pill { display: inline-flex; align-items: center; justify-content: center; } .custom-ag-grid .status-active { color: #047857; } .custom-ag-grid .status-blocked { color: #b91c1c; } .custom-ag-grid .status-complete { color: #1d4ed8; } .custom-ag-grid .status-planning { color: #a16207; } ``` The same approach works for header cells (`headerClass`) and rows (`rowClass` / `rowClassRules` on the grid itself). # AG Grid Tree Data in Python Source: http://localhost:3000/docs/enterprise/ag-grid/tree-data.md --- meta_description: "Display hierarchical data in AG Grid with Reflex. Build tree views like file explorers with expandable rows, group aggregation, and custom data paths in pure Python." title: Tree Data --- Tree data displays hierarchical data — like a file system — with expandable and collapsible nodes. Each row provides a *path* (an array like `["Documents", "Projects", "file.txt"]`) that determines its position in the tree. To enable it, set `tree_data=True` and tell the grid where to find the path with either `data_path_key` or `get_data_path` (specify exactly one, not both). ## Simple Case: `data_path_key` When a field in the row data already contains the path array, pass its name as a string via `data_path_key`: ```python demo exec import reflex as rx import reflex_enterprise as rxe class TreeDataState(rx.State): data: list[dict] = [ { "path": ["Documents", "Projects", "file1.txt"], "size": 1024, "created": "2023-10-01", }, { "path": ["Documents", "Projects", "subfolder", "file2.py"], "size": 2048, "created": "2023-10-02", }, { "path": ["Downloads", "image.jpg"], "size": 512000, "created": "2023-10-03", }, ] def tree_grid_simple(): return rxe.ag_grid( id="tree_grid_simple", row_data=TreeDataState.data, tree_data=True, data_path_key="path", auto_group_column_def={ "headerName": "File Path", "minWidth": 280, "cellRendererParams": {"suppressCount": True}, }, column_defs=[ {"field": "size", "aggFunc": "sum"}, {"field": "created"}, ], group_default_expanded=0, # 0 = collapsed, -1 = all expanded width="100%", height="500px", ) ``` Key configuration: - `auto_group_column_def` configures the tree column (the one with the expand/collapse arrows). - `group_default_expanded` controls the initial expansion: `0` for collapsed, `-1` for fully expanded, or a positive number for the depth to expand to. - `aggFunc` on value columns aggregates values at the group level (e.g. total size of a folder). ## Custom Paths: `get_data_path` For anything beyond reading a single field, pass a JavaScript function as `get_data_path`, built with `rx.vars.function.ArgsFunctionOperation`: ```python demo exec def tree_grid_custom(): return rxe.ag_grid( id="tree_grid_custom", row_data=TreeDataState.data, tree_data=True, get_data_path=rx.vars.function.ArgsFunctionOperation.create( ["data"], rx.Var("data.path"), ), auto_group_column_def={"headerName": "File Path", "minWidth": 280}, column_defs=[ {"field": "size", "aggFunc": "sum"}, {"field": "created"}, ], group_default_expanded=0, width="100%", height="500px", ) ``` The function receives the row `data` and must return the array representing the row's position in the hierarchy. The JavaScript expression can build the array on the fly, e.g. `rx.Var("[data.host, ...data.path]")` to prefix each path with a host name. ```md alert warning # Pass `get_data_path` directly as a grid prop — never define it as a state var or computed var. Returning the function from an `@rx.var` (or storing it in state) raises `Invalid var passed for prop WrappedAgGrid.get_data_path`. Build the `ArgsFunctionOperation` inline in the `rxe.ag_grid(...)` call. ``` ### Switching Paths Dynamically `get_data_path` can be selected at render time with `rx.cond`. Give the grid a `key` derived from the condition so it re-initializes when the path logic changes: ```python demo exec class TreeDisplayState(rx.State): combine_hosts: bool = True data: list[dict] = [ {"host": "server1", "path": ["Documents", "file1.txt"], "size": 1024}, {"host": "server2", "path": ["Downloads", "file2.pdf"], "size": 2048}, ] @rx.event def set_combine_hosts(self, value: bool): self.combine_hosts = value def tree_grid_conditional(): return rx.vstack( rx.hstack( rx.switch( checked=TreeDisplayState.combine_hosts, on_change=TreeDisplayState.set_combine_hosts, ), rx.text("Combine both hosts into one tree"), align="center", ), rxe.ag_grid( id="tree_grid_conditional", row_data=TreeDisplayState.data, tree_data=True, get_data_path=rx.cond( TreeDisplayState.combine_hosts, rx.vars.function.ArgsFunctionOperation.create( ["data"], rx.Var("data.path"), ), rx.vars.function.ArgsFunctionOperation.create( ["data"], rx.Var("[data.host, ...data.path]"), ), ).to(rx.vars.FunctionVar), key=f"grid_{TreeDisplayState.combine_hosts}", auto_group_column_def={"headerName": "File Explorer", "minWidth": 280}, column_defs=[{"field": "size", "aggFunc": "sum"}], width="100%", height="500px", ), width="100%", ) ``` ## Formatting Aggregated Values Value formatters work on tree columns too. For example, human-readable file sizes: ```python human_size = rx.vars.function.ArgsFunctionOperation.create( ["params"], rx.Var("""{ const sizeInKb = params.value / 1024; if (sizeInKb > 1024) { return `${+(sizeInKb / 1024).toFixed(2)} MB`; } else { return `${+sizeInKb.toFixed(2)} KB`; } }"""), ) column_defs = [ {"field": "size", "aggFunc": "sum", "value_formatter": human_size}, ] ``` # AG Grid Value Transformers in Python Source: http://localhost:3000/docs/enterprise/ag-grid/value-transformers.md --- meta_description: "Transform and format AG Grid cell values in Python with Reflex. Use value getters, formatters, and setters to control how data displays in your data grid." order: 2 --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` AgGrid allow you to apply transformers based on the column of your grid. This allow you to perform operations on the data before displaying it on the grid, without having to pre-process the data on the backend, reducing the load on your application. ## Value Getter `value_getter` is a property of the column definition that allows you to define a function that will be called to get the value of the cell. This function will receive the row data as a parameter and should return the value to be displayed on the cell. If you have two columns `col_a` and `col_b` and you want to display the sum of these two columns in a third column `sum`, you can define the `value_getter` of `sum` as follows: ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.DataFrame({"col_a": [1, 2, 3, 4, 5], "col_b": [10, 20, 30, 40, 50]}) column_defs = [ {"field": "col_a", "header_name": "Column A"}, {"field": "col_b", "header_name": "Column B"}, { "field": "sum", "header_name": "Sum", "value_getter": "params.data.col_a + params.data.col_b", }, rxe.ag_grid.column_def( field="diff", header_name="Difference", value_getter="params.data.col_b - params.data.col_a", ), ] def ag_grid_value_getter(): return rxe.ag_grid( id="ag_grid_value_getter", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", ) ``` ## Value Formatter `value_formatter` is a property of the column definition that allows you to define a function that will be called to format the value of the cell. This function will receive the value of the cell as a parameter and should return the formatted value to be displayed on the cell. If you have a column `price` and you want to display the price with a currency symbol, you can define the `value_formatter` of `price` as follows: ```python demo exec import reflex as rx import reflex_enterprise as rxe import pandas as pd df = pd.DataFrame({ "product_name": ["Product A", "Product B", "Product C", "Product D", "Product E"], "price": [100, 200, 300, 400, 500], }) column_defs = [ {"field": "product_name", "header_name": "Product Name"}, { "field": "price", "header_name": "Price ($)", "value_formatter": "'$' + params.value", }, rxe.ag_grid.column_def( col_id="price_eur", header_name="Price (€)", value_formatter="params.data.price + ' €'", ), ] def ag_grid_value_formatter(): return rxe.ag_grid( id="ag_grid_value_formatter", row_data=df.to_dict("records"), column_defs=column_defs, width="100%", ) ``` ## Formatter Patterns Formatters and getters can be written in a few different styles. Simple inline JavaScript expressions are the most reliable and should be preferred: ```python column_defs = [ {"field": "name", "value_formatter": "params.value.toUpperCase()"}, {"field": "price", "value_formatter": "'$' + params.value.toFixed(2)"}, {"field": "percent", "value_formatter": "(params.value * 100).toFixed(1) + '%'"}, {"field": "date", "value_formatter": "new Date(params.value).toLocaleDateString()"}, # Conditional logic works inline with a ternary {"field": "score", "value_formatter": "params.value > 100 ? 'High' : 'Low'"}, ] ``` Python lambdas are useful for basic type conversions — the lambda receives a `params` var and operates on it symbolically: ```python column_defs = [ { "field": "number", "value_formatter": lambda params: round(params.value.to(float), 2), }, {"field": "status", "value_formatter": lambda params: params.value.to(str).title()}, ] ``` Short arrow functions can also be passed via `rx.vars.FunctionStringVar`: ```python CURRENCY_FORMATTER = rx.vars.FunctionStringVar.create( "(params) => '$' + params.value.toFixed(2)" ) column_defs = [{"field": "price", "value_formatter": CURRENCY_FORMATTER}] ``` Keep JavaScript formatters short: multi-line function bodies with complex conditionals often fail to render. If the logic doesn't fit a simple expression, compute the value on the backend instead, or use a [cell renderer](#cell-renderer). Formatters and getters are always passed directly in the column definition — they are not registered as AG Grid "components". ## Cell Renderer While formatters change the displayed text, `cell_renderer` replaces the cell contents with a Reflex component. The renderer is a lambda that receives the cell `params` and returns a component: ```python column_defs = [ { "field": "number", "cell_renderer": lambda params: rx.text( params.value, font_family="monospace", color="rebeccapurple", ), }, # params.valueFormatted holds the output of the column's value_formatter { "field": "total", "cell_renderer": lambda params: rx.tooltip( rx.text(params.valueFormatted, line_height="inherit", width="fit-content"), content=f"{params.data.number} * {params.data.percent}", side="left", ), }, ] ``` ### Interactive Cell Renderers Renderers that use state or event handlers must be defined as an `@rx.memo` component. Pass it to `cell_renderer` through a lambda, with all arguments given by keyword — never pass the memoized function itself directly: ```python @rx.memo def row_action_button(rowid: rx.Var[str]) -> rx.Component: return rx.flex( rx.button( RowClickCounterState.row_clicks.get(rowid, 0), on_click=RowClickCounterState.handle_click(rowid), ), height="100%", align="center", ) column_defs = [ { "field": "actions", "cell_renderer": lambda params: row_action_button(rowid=params.node.id), }, ] ``` # Customizing the Auth Pages Source: http://localhost:3000/docs/enterprise/auth/custom-pages.md --- title: Customizing the Auth Pages --- _New in reflex-enterprise v0.9.1._ `rxe.AuthPlugin` registers four auth routes and owns their protocol wiring. The component rendered on each route is customizable; the OIDC redirect, callback token exchange, and logout dispatch remain plugin-owned. | Endpoint | Default route | Plugin-owned wiring | Builder | | --- | --- | --- | --- | | `login_endpoint` | `/login` | Renders the login palette and starts the OIDC redirect. | `login_page` | | `auth_callback_endpoint` | `/callback` | CSRF (OAuth `state`) check + authorization-code token exchange, then redirect back. | `callback_page` | | `logout_endpoint` | `/logout` | Dispatches the active provider's logout; a CSRF guard blocks cross-site logout (see [secure by default](/docs/enterprise/auth/secure-by-default/#logout-is-protected-against-csrf)). | `logout_page` | | `forbidden_endpoint` | `/forbidden` | Shown when an authenticated user lacks permission to view a page. | `forbidden_page` | The routes themselves are configurable through `login_endpoint`, `logout_endpoint`, `auth_callback_endpoint`, and `forbidden_endpoint`. See the [providers](/docs/enterprise/auth/providers/) page for configuring identity providers, and the [overview](/docs/enterprise/auth/overview/) for how the plugin fits together. ```md alert warning # Register the callback URI with your IdP If you change `auth_callback_endpoint`, register that exact URI as the OAuth redirect URI with your identity provider, or the token exchange will be rejected. ``` ## The page builder contract A page builder is a callable that receives the build context as **keyword arguments**: | Keyword | Type | Meaning | | --- | --- | --- | | `providers` | `Sequence[type[OIDCAuthState]]` | The resolved provider state classes. | | `plugin` | `AuthPlugin` | The plugin instance. | Name the required entries and add `**context` to ignore the rest: ```python import reflex as rx def custom_login_page(providers, **context) -> rx.Component: ... ``` A builder may also accept only `**context`. The same contract applies to the login, callback, logout, and forbidden builders. ## A custom login page In most apps, send users to `/login`. Customize `login_page` when the default login buttons need a different layout. Call each provider's `get_login_button(*children)` and pass the clickable element as children. This preserves the OIDC redirect wiring and the iframe popup listener. `provider.display_name()` returns the provider label; by default it is the title-cased `__provider__` value: ```python import reflex as rx def custom_login_page(providers, **context) -> rx.Component: return rx.center( rx.vstack( rx.heading("Sign in"), *[ provider.get_login_button( rx.button(f"Continue with {provider.display_name()}") ) for provider in providers ], spacing="4", ), min_height="60vh", ) ``` With two or more providers, render one `get_login_button()` per provider. See [running inside an iframe](/docs/enterprise/auth/providers/#running-inside-an-iframe) for the popup flow requirement. ## Custom callback and logout pages The callback and logout routes only show an interstitial while their plugin-owned `on_load` runs. Reuse `providers[0].get_authentication_loading_page()`, which already shows the validating and redirecting states as the exchange (or logout) proceeds, plus an error view if it fails (see [auth-failure UX and troubleshooting](#auth-failure-ux-and-troubleshooting)): ```python import reflex as rx def custom_callback_page(providers, **context) -> rx.Component: return rx.center( rx.vstack( rx.text("Completing sign-in…"), providers[0].get_authentication_loading_page(), ), min_height="60vh", ) def custom_logout_page(providers, **context) -> rx.Component: return providers[0].get_authentication_loading_page() ``` Wrap that view in an app-specific layout when the interstitial needs branding. ## Auth-failure UX and troubleshooting When a token exchange or validation fails, `get_authentication_loading_page()` swaps its spinner for an error view: a user-facing message plus an **error ID** (a per-flow UUID) the user can hand to support. The same failure is logged on the backend at `ERROR` level, prefixed ` [txid=]`. The log entry is emitted even when the app configures no logging. Use the displayed ID to find the matching server log. ```md alert info # Operator note When a user reports a failed login, search the backend logs for `[txid=...]` with the ID they were shown. ``` The page builders do **not** take an error override. `default_callback_page` calls `get_authentication_loading_page()`. There are two supported ways to customize the failure UI: **1. Override the state classmethods.** Subclass your provider state and override `get_error_component`, `get_authentication_error_component`, or `get_logout_error_component`. The loading page picks up the override automatically: ```python import reflex as rx from reflex_enterprise.auth import GenericOIDCAuthState class MyProviderState(GenericOIDCAuthState): @classmethod def get_error_component(cls, operation, suggestion, error_id) -> rx.Component: return rx.vstack( rx.heading("Something went wrong"), rx.text(suggestion), rx.text("Error ID: ", rx.badge(error_id)), ) ``` **2. Hand-write a page reading the public vars.** `has_error`, `user_error_message`, and `last_error_txid` are public Vars on the provider state, A custom callback or logout builder can branch on them directly: ```python import reflex as rx def custom_callback_page(providers, **context) -> rx.Component: provider = providers[0] return rx.center( rx.cond( provider.has_error, rx.vstack( rx.heading("Sign-in failed"), rx.text(provider.user_error_message), rx.text("Error ID: ", rx.badge(provider.last_error_txid)), ), provider.get_authentication_loading_page(), ), min_height="60vh", ) ``` ## A custom forbidden page `/forbidden` is shown when an **authenticated** user tries to load a page they are not authorized to view. This happens when the global default `AuthPlugin(auth=...)` is a callable check that fails on a page load. The forbidden page has no plugin-owned `on_load`. ```python import reflex as rx def custom_forbidden_page(**context) -> rx.Component: return rx.center( rx.vstack( rx.heading("403", size="8"), rx.text("Access denied."), rx.link("Back to home", href="/"), spacing="3", align="center", ), min_height="60vh", ) ``` ```md alert info # When does the forbidden page appear? Only on a **page** load that an authenticated user fails through a callable global default. Failed event-handler checks show an `"Action not allowed"` toast. Failed field and var checks withhold the value. Neither navigates to `/forbidden`. See [authentication vs authorization](/docs/enterprise/auth/secure-by-default/#authentication-vs-authorization). ``` ## Wiring them up Pass the builders to the plugin in `rxconfig.py` as **import-path strings** (`"module.function"`). The builder modules import `reflex_enterprise`, which loads `rxconfig` at import time. Importing them directly in `rxconfig.py` would re-enter the config. Import-path strings are resolved lazily at compile time: ```python import reflex_enterprise as rxe config = rxe.Config( app_name="my_app", plugins=[ rxe.AuthPlugin( login_page="my_app.auth_pages.custom_login_page", callback_page="my_app.auth_pages.custom_callback_page", logout_page="my_app.auth_pages.custom_logout_page", forbidden_page="my_app.auth_pages.custom_forbidden_page", ), ], ) ``` ```md alert info # Strings in rxconfig, callables elsewhere The import-path string is only required in `rxconfig.py`. Where the builder is already importable, pass the callable directly: `login_page=custom_login_page`. ``` ## Defaults Omit a builder and the plugin falls back to its defaults from `reflex_enterprise.auth.pages`: | Builder argument | Default | Renders | | --- | --- | --- | | `login_page` | `default_login_page` | One `provider.get_login_button()` per provider. | | `callback_page` | `default_callback_page` | `providers[0].get_authentication_loading_page()`. | | `logout_page` | `default_logout_page` | `providers[0].get_authentication_loading_page()`. | | `forbidden_page` | `default_forbidden_page` | A 403 access denied view. | The defaults take the same keyword context. A custom builder may call a default builder and wrap the returned content: ```python import reflex as rx from reflex_enterprise.auth import default_login_page def custom_login_page(providers, **context) -> rx.Component: return rx.center(default_login_page(providers=providers, **context)) ``` ## Related - [Providers](/docs/enterprise/auth/providers/): identity provider configuration. - [Secure by default](/docs/enterprise/auth/secure-by-default/): protected surfaces and `/forbidden` behavior. - [Testing](/docs/enterprise/auth/testing/): guarded-surface tests. # Deploying to Production Source: http://localhost:3000/docs/enterprise/auth/deployment.md --- title: Deploying to Production --- _New in reflex-enterprise v0.9.1._ The [quickstart](/docs/enterprise/auth/overview/) works on `localhost` out of the box, but moving the same app to a real deployment against a real identity provider (IdP) has three requirements: serve the app over HTTPS, register the exact callback URL with the IdP, and report the public origin correctly behind reverse proxies. ## HTTPS is required All four auth cookies (access token, ID token, refresh token, and granted scopes) are issued `Secure; SameSite=Strict; HttpOnly`. These attributes are fixed and **not configurable**. Because browsers drop `Secure` cookies sent over plain HTTP, the app **must** be served to the browser over HTTPS in any non-localhost deployment. Local `reflex run` works without TLS only because browsers exempt `localhost` from the `Secure` rule. ```md alert warning # Symptom: an apparent login loop If the app is served over plain HTTP on a non-localhost host, the IdP redirect to `/callback` completes but the browser discards the `Secure` auth cookies. The user is not authenticated and is sent back to `/login`. Serve the app over HTTPS. ``` Behind a TLS-terminating reverse proxy, terminate TLS at the proxy and ensure the browser-facing origin is `https`. Browsers enforce `Secure` based on the origin visible to the browser. ## Registering the callback URL The OAuth redirect URI is built at runtime from the browser-visible page URL, with the path replaced by the plugin's `auth_callback_endpoint` (`/callback` by default). It is **not** a config value: its scheme, host, and port follow the request origin. This is the concrete version of step 3 in the [overview](/docs/enterprise/auth/overview/). Register the **full** callback URL, including scheme, host, port, and path, as an allowed redirect URI in your IdP's client settings, and register every origin you actually use: ```bash http://localhost:3000/callback # dev https://your-app.com/callback # prod ``` A redirect URI that does not match a registered value produces the IdP's most common error, `redirect_uri_mismatch`. ```md alert info # Behind a TLS-terminating proxy Make sure the app sees the request as `https` through forwarded-proto handling at the proxy. Otherwise it builds an `http://...` redirect URI that does not match the registered `https://...` value. ``` ## Behind a reverse proxy / at scale Two properties of the auth model determine how it behaves behind a proxy or across multiple replicas. **The public origin must be correct.** Both the redirect URI and the post-login index URI are derived from the browser-visible request origin. A reverse proxy must forward the real public scheme and host. If the proxy reports `http` or an internal hostname, the runtime redirect URI won't match what's registered at the IdP. The backend (`api_url`) must also be reachable from the browser: after login the client makes a credentialed request to `/_reflex/cookies/sync` to persist the auth cookies. **No sticky sessions are needed.** Auth secrets live in client-side HTTP cookies. A LocalStorage flag signals other tabs to re-pull the cookies. There is no server-side session store, so multiple backend replicas can run behind a load balancer without sticky-session configuration. Give every replica the same OIDC client configuration (the same `OIDC_*` / `{PROVIDER}_*` environment variables). ## Connecting a real identity provider Every provider is discovered from its issuer's `.well-known/openid-configuration`, always runs the Authorization Code flow with PKCE (`S256`), and requests `openid email profile` by default. Because `client_secret` is optional, each IdP client can be either **public** (PKCE only, no secret) or **confidential** (PKCE plus a secret). The mechanics are the same for every IdP; only the issuer URL changes. A worked example with Google: 1. In the Google Cloud console, create an **OAuth 2.0 Client ID** of type **Web application**. 2. Add your callback URLs (see [registering the callback URL](#registering-the-callback-url)) as **Authorized redirect URIs**, e.g. `https://your-app.com/callback`. 3. Configure the app. Because Google issues a client secret, run the client as confidential by setting all three variables (omit `OIDC_CLIENT_SECRET` to run PKCE-public instead): ```bash OIDC_ISSUER_URI=https://accounts.google.com OIDC_CLIENT_ID=your-client-id.apps.googleusercontent.com OIDC_CLIENT_SECRET=your-client-secret ``` 4. To receive group claims for authorization checks, add the relevant scope via `extra_scopes` (see [providers](/docs/enterprise/auth/providers/#scopes-and-refresh-tokens)). Other IdPs follow the same pattern with their own issuer URL: | IdP | `OIDC_ISSUER_URI` form | | --- | --- | | Google | `https://accounts.google.com` | | Azure AD | `https://login.microsoftonline.com//v2.0` | | Auth0 | `https://.auth0.com` | | Okta | `https://.okta.com` | ## Troubleshooting | Symptom | Cause | Fix | | --- | --- | --- | | `redirect_uri_mismatch` (from the IdP) | The redirect URI is derived from the request scheme + host + `auth_callback_endpoint`; the value registered at the IdP does not match it. | Register the exact callback URL; behind a proxy, make sure the app sees the public scheme and host. | | `invalid_client` (from the IdP at token exchange) | The IdP client is confidential, but `OIDC_CLIENT_SECRET` is unset. It defaults to empty, and no local startup error is raised. | Set the client secret, or switch the IdP client to public / PKCE. | | Logged in but bounced back to `/login` (login loop) | `Secure; SameSite=Strict` cookies are dropped over plain HTTP on a non-localhost host. | Serve the app over HTTPS (`localhost` is exempt). | | `ConfigError` at startup | Either a plain `rx.App()` was used with the plugin, or two or more providers both fall back to the shared `OIDC_*` config for issuer or client id. | Use `rxe.App()`; for multiple providers, set provider-specific `{PROVIDER}_*` variables (see [providers](/docs/enterprise/auth/providers/#multiple-providers)). | ## Related - [Authentication overview](/docs/enterprise/auth/overview/): quickstart and login flow. - [Secure by default](/docs/enterprise/auth/secure-by-default/): enforcement and authorization checks. - [Providers](/docs/enterprise/auth/providers/): provider configuration, environment variables, scopes, and multi-provider setups. - [Custom pages](/docs/enterprise/auth/custom-pages/): custom login, callback, logout, and forbidden page builders. - [Testing](/docs/enterprise/auth/testing/): guarded-surface and mock-IdP tests. # Authentication Overview Source: http://localhost:3000/docs/enterprise/auth/overview.md --- title: Authentication Overview --- _New in reflex-enterprise v0.9.1._ `rxe.AuthPlugin` adds [OIDC](https://openid.net/developers/how-connect-works/) (OpenID Connect) authentication to Reflex apps. Add the plugin to `rxe.Config(plugins=[...])`, set the provider environment variables, and use `rxe.App()`. By default, every page, event handler, base field, and computed var requires an authenticated user. Use `auth=False` for surfaces that should be public. The plugin runs the OIDC Authorization Code + PKCE flow against your identity provider (IdP) and registers `/login`, `/logout`, `/callback`, and `/forbidden` routes. ```md alert warning # Requirements The auth plugin ships with `reflex-enterprise` (v0.9.1+). Your app **must** use `rxe.App()` (not `rx.App()`), and you must configure an OIDC identity provider via environment variables. Using a plain `rx.App()` with the plugin raises a `ConfigError` at startup. ``` ## Quickstart **1. Add `rxe.AuthPlugin()` to `rxconfig.py`** and configure your OIDC provider through the `OIDC_*` environment variables: ```python import os import reflex as rx import reflex_enterprise as rxe os.environ.setdefault("OIDC_ISSUER_URI", "https://your-idp.example.com") os.environ.setdefault("OIDC_CLIENT_ID", "your-client-id") os.environ.setdefault("OIDC_CLIENT_SECRET", "your-client-secret") # optional with PKCE config = rxe.Config( app_name="my_app", plugins=[ rxe.AuthPlugin(), ], ) ``` With the `OIDC_*` variables set, the app imports and compiles before the IdP is reachable. OIDC discovery runs only when a user logs in. Placeholder values are enough for local builds and CI. **2. Use `rxe.App()`** (not `rx.App()`) in your app module: ```python import reflex_enterprise as rxe app = rxe.App() ``` **3. Register the redirect URI with your IdP.** Add the plugin's `auth_callback_endpoint` (`/callback` by default) as an allowed redirect URI in your identity provider's client settings. Register the **full** URL (scheme, host, and path) for each environment. A mismatched value produces `redirect_uri_mismatch`; see [deploying to production](/docs/enterprise/auth/deployment/) for the exact callback URL. You don't need to write a provider class. The plugin uses the built-in `GenericOIDCAuthState`, which reads those variables. See [providers](/docs/enterprise/auth/providers/) for named and multi-provider setups. ## The four protected surfaces Once active, the plugin protects four kinds of surface by default: | Surface | Default | How it's withheld | Opt out / gate | | --- | --- | --- | --- | | Pages (`@rxe.page` / `@rx.page` / `app.add_page`) | login required | redirect to `/login` | `auth=False`, or `@rxe.page(auth=True)` to force login | | Event handlers (`@rxe.event`) | login required | block + redirect/toast | `@rxe.event(auth=False)` or `auth=` | | Base fields (`rxe.field` / plain `rx.field`) | withheld until login | replaced with its declared default | `rxe.field(default, auth=False)` or `auth=` | | Computed vars (`@rxe.var`) | withheld until login | replaced with its `initial_value` (dropped if it has none) | `@rxe.var(auth=False)` or `auth=` | `auth=True` is the default everywhere, so a plain `rx.field(...)` or bare `@rxe.var` is already protected. Set `auth=False` to make a surface public. ```python import reflex as rx import reflex_enterprise as rxe class DashboardState(rx.State): # Protected by default; withheld from the client until login. revenue: rx.Field[float] = rx.field(0.0) # Public; always sent to the client. theme: rx.Field[str] = rxe.field("light", auth=False) @rxe.event # default auth=True: anonymous callers are redirected to /login async def refresh(self): ... @rxe.event(auth=False) # public handler anyone may call def toggle_theme(self): self.theme = "dark" if self.theme == "light" else "light" ``` See [secure by default](/docs/enterprise/auth/secure-by-default/) for the full enforcement model, the four `auth=` wrappers, and authorization check functions. ## Reading the current user `reflex_enterprise.auth.User` is an alias of `reflex_enterprise.auth.AuthUserState`. Use its class-level Vars directly in components. They are populated by the provider that authenticated the user: ```python import reflex as rx from reflex_enterprise.auth import User def profile() -> rx.Component: return rx.hstack( rx.avatar(src=User.picture, fallback="U"), rx.vstack( rx.heading(User.name), rx.text(User.email, color_scheme="gray"), ), ) ``` Inside an event handler, `await User.current()` returns the user's `OIDCUserInfo` claims dict (or `None` when anonymous): ```python import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.auth import User class GreetState(rx.State): @rxe.event # default auth=True async def greet(self): user = await User.current() or {} return rx.toast(f"Hello {user.get('name') or user.get('sub')}!") ``` See [reading the current user](/docs/enterprise/auth/secure-by-default/#reading-the-current-user) for the full `User` API, including frontend Vars, `current()`, and `current_provider()`. ## Signing out Sign the user out by linking to `/logout` or by binding the `User.logout` event: ```python import reflex as rx from reflex_enterprise.auth import User # Either link to the logout route. rx.link("Sign out", href="/logout") # Or bind the logout event to any component. rx.button("Sign out", on_click=User.logout) ``` `User.logout` signs out whichever provider the user logged in with, so it works with multiple providers. If no one is signed in there's nothing to sign out, so it just sends them back to the home page (`/`). Example header: ```python import reflex as rx from reflex_enterprise.auth import User def header() -> rx.Component: return rx.cond( User.sub != "", rx.hstack( rx.text(f"Signed in as {User.name}"), rx.link("Sign out", href="/logout"), ), rx.link("Log in", href="/login"), ) ``` ## How a login flows end to end 1. An anonymous visitor hits a protected page (or calls a protected handler) and is redirected to `/login`, with the requested page preserved as a `redirect_to` query parameter. 2. `/login` renders a button per configured provider. The visitor clicks one and is sent to the IdP's authorization endpoint (Authorization Code + PKCE). 3. The IdP authenticates the user and redirects back to `/callback`, which validates the OAuth `state` (CSRF), exchanges the code for tokens, and stores them in secure cookies. 4. The user is redirected back to `redirect_to`. Protected fields, vars, pages, and handlers now resolve against the authenticated user. 5. `/logout` clears tokens, resets the protected surface of every state, and chains the provider's logout. A CSRF guard blocks cross-site logout requests (see [secure by default](/docs/enterprise/auth/secure-by-default/#logout-is-protected-against-csrf)). ## Related - [Secure by default](/docs/enterprise/auth/secure-by-default/): enforcement, `auth=`, authorization checks, and the `User` facade. - [Providers](/docs/enterprise/auth/providers/): provider classes, environment variables, scopes, and multi-provider setups. - [Custom pages](/docs/enterprise/auth/custom-pages/): custom `/login`, `/callback`, `/logout`, and `/forbidden` components. - [Testing](/docs/enterprise/auth/testing/): authorization checks and mock-IdP integration tests. - [Deploying to production](/docs/enterprise/auth/deployment/): HTTPS, callback URLs, reverse proxies, and troubleshooting. - [Enterprise overview](/docs/enterprise/overview/): other Enterprise features. # OIDC Providers Source: http://localhost:3000/docs/enterprise/auth/providers.md --- title: OIDC Providers --- _New in reflex-enterprise v0.9.1._ An OIDC provider is the state class that runs the OpenID Connect Authorization Code + PKCE flow against your identity provider (IdP). `rxe.AuthPlugin` ships a built-in provider that resolves its configuration from environment variables. See [secure by default](/docs/enterprise/auth/secure-by-default/) for how the plugin protects pages, events, fields, and computed vars, and the [overview](/docs/enterprise/auth/overview/) for setup. ## The default provider `GenericOIDCAuthState` is the built-in provider. It reads three environment variables: ```bash OIDC_ISSUER_URI=https://your-issuer.example.com OIDC_CLIENT_ID=your-client-id OIDC_CLIENT_SECRET=your-client-secret # optional; PKCE works without it ``` `AuthPlugin.auth_providers` defaults to `[GenericOIDCAuthState]`. With the `OIDC_*` variables set, no provider class is required: ```python import reflex as rx import reflex_enterprise as rxe config = rxe.Config( app_name="my_app", plugins=[rxe.AuthPlugin()], # uses GenericOIDCAuthState + OIDC_* env vars ) ``` Register the plugin's `auth_callback_endpoint` (default `/callback`) as the redirect URI with your IdP. ## Naming a provider To use provider-specific environment variables (and to register multiple distinct IdPs), subclass `OIDCAuthState` and set `__provider__`. The subclass must also inherit `rx.State`: ```python import reflex as rx from reflex_enterprise.auth import OIDCAuthState class OktaAuthState(OIDCAuthState, rx.State): __provider__ = "okta" ``` With `__provider__ = "okta"`, config resolution prefers the `OKTA_ISSUER_URI` / `OKTA_CLIENT_ID` / `OKTA_CLIENT_SECRET` variables, falling back to the shared `OIDC_*` keys: ```bash OKTA_ISSUER_URI=https://your-org.okta.com OKTA_CLIENT_ID=your-okta-client-id OKTA_CLIENT_SECRET=your-okta-client-secret ``` After the provider is registered, the default `/login` page shows a login button for it. `display_name()` controls the button label. By default, it returns the title-cased `__provider__` value (`"okta"` -> `"Okta"`): ```python class OktaAuthState(OIDCAuthState, rx.State): __provider__ = "okta" @classmethod def display_name(cls) -> str: return "Okta SSO" ``` ## Running inside an iframe Embedded apps use a popup login/logout flow instead of top-level redirects. The popup is opened from the user's click and posts tokens back to the app's own origin with `postMessage`. When customizing `/login`, call `provider.get_login_button(*children)` for each provider. This mounts the message listener used by the popup flow. Calling `redirect_to_login` directly does not mount that listener. In most apps, link users to `/login` and customize the login page when the default layout is not enough. See [custom pages](/docs/enterprise/auth/custom-pages/#a-custom-login-page). Override `_use_popup_flow(self) -> bool` on the provider subclass to force or disable the popup flow. The default returns whether the app is embedded. ## Environment variables Each provider resolves every config key by trying the provider-specific `{PROVIDER}_{KEY}` variable first, then falling back to the shared `OIDC_{KEY}`. `{PROVIDER}` is the uppercased `__provider__`. | Key | Provider-specific | Shared fallback | Notes | | --- | --- | --- | --- | | Issuer | `{PROVIDER}_ISSUER_URI` | `OIDC_ISSUER_URI` | The IdP issuer URL (its `.well-known/openid-configuration` is discovered from here). | | Client ID | `{PROVIDER}_CLIENT_ID` | `OIDC_CLIENT_ID` | The OAuth client id. | | Client Secret | `{PROVIDER}_CLIENT_SECRET` | `OIDC_CLIENT_SECRET` | Optional; PKCE works without it. | The default `GenericOIDCAuthState` (`__provider__ = "generic"`) resolves `GENERIC_*` then `OIDC_*`. For the default provider, set only the `OIDC_*` keys. ## Registering providers with the plugin `AuthPlugin(auth_providers=[...])` accepts provider **classes** or `"module.ClassName"` import-path **strings**. Strings are resolved lazily at compile time. Order is preserved, and the two forms may be mixed. The default is `[GenericOIDCAuthState]`. ```md alert warning # Use import-path strings in `rxconfig.py` Provider modules import `reflex_enterprise`, which loads `rxconfig` at import time. Importing a provider class directly in `rxconfig.py` would re-enter the config. Pass providers as `"module.ClassName"` strings there. The plugin resolves them lazily once the config exists. ``` In `rxconfig.py`, pass strings: ```python import reflex_enterprise as rxe config = rxe.Config( app_name="my_app", plugins=[ rxe.AuthPlugin( auth_providers=["my_app.auth.OktaAuthState"], ), ], ) ``` Outside `rxconfig.py` (for example, in tests) you may pass the classes themselves: ```python from my_app.auth import OktaAuthState rxe.AuthPlugin(auth_providers=[OktaAuthState]) ``` ## Scopes and refresh tokens `extra_scopes` is forwarded to every configured provider and merged into the scopes each one requests. The merge is deduped and preserves the existing scopes (`openid email profile` by default): ```python rxe.AuthPlugin( auth_providers=["my_app.auth.OktaAuthState"], extra_scopes=["offline_access"], ) ``` - `extra_scopes=["offline_access"]` asks the IdP to issue a **refresh token**. Once granted, the framework refreshes the access token as it nears expiry, coordinated across browser tabs. Refresh requests include only scopes the IdP originally **granted**; scopes consumed but not granted by the IdP, such as `offline_access`, do not trigger `invalid_scope` during refresh. - Without `offline_access` there is no refresh token. The session ends when the access token expires, and the user is returned to `/login`. - If a refresh fails (the refresh token was revoked or expired), the session is reset and the user is logged out. - `extra_scopes=["groups"]` requests group claims, useful for authorization checks against `ctx.auth_user_state.userinfo.get("groups")`. Independent of token expiry, every auth cookie has a fixed 7-day lifetime. This is the effective maximum session length. See the [deployment guide](/docs/enterprise/auth/deployment/) for the HTTPS and cookie requirements. ### Per-provider scopes `extra_scopes` is applied uniformly to every configured provider and only **adds** to the defaults. To give a single provider a distinct set, set the `_requested_scopes` class attribute on that subclass. Unlike `extra_scopes`, this **replaces** the default `"openid email profile"` rather than merging into it. ```python import reflex as rx from reflex_enterprise.auth import OIDCAuthState class DatabricksAuthState(OIDCAuthState, rx.State): __provider__ = "databricks" _requested_scopes: str = "all-apis offline_access openid email profile" ``` ### Reading granted scopes Every provider exposes a `granted_scopes` Var holding the space-delimited scopes the IdP actually granted. `await User.current_provider()` resolves whichever provider authenticated the current user, so derive a computed var on your own state that reads the active provider's scopes — then `rx.cond` gates on it, regardless of which provider the user logged in with: ```python import reflex as rx from reflex_enterprise.auth import User class DemoState(rx.State): @rx.var(initial_value=False, auto_deps=False, deps=[]) async def has_offline_access(self) -> bool: """Whether the active provider was granted `offline_access`.""" provider = await User.current_provider() if provider is None: return False scopes = (await self.get_state(provider)).granted_scopes.split() return "offline_access" in scopes ``` ```python rx.cond( DemoState.has_offline_access, rx.text("Long-lived session enabled"), rx.text("Session ends at token expiry"), ) ``` Granted scopes are fixed once the user logs in, so the var carries no reactive dependencies (`auto_deps=False, deps=[]`): it resolves on login and stays stable for the session. ## Multiple providers `/login` shows one login button per provider, including when there is only one — it never redirects to an IdP automatically. The callback resolves the **initiating** provider from the OAuth `state` parameter; logout resolves the **active** provider currently holding tokens. ```python rxe.AuthPlugin( auth_providers=[ "my_app.auth.OktaAuthState", "my_app.auth.AzureAuthState", ], ) ``` ```md alert warning # Give each provider its own config when running more than one If two or more providers fall back to the shared `OIDC_*` config for a required key (issuer or client id), they would resolve to the same IdP value. The plugin raises a `ConfigError` at startup naming the affected providers. Set provider-specific `{PROVIDER}_*` variables, such as `OKTA_ISSUER_URI` and `AZURE_ISSUER_URI`, for each. ``` Reading the user is provider-agnostic: `User.name` / `.email` / `.sub` / `.picture` bind to `AuthUserState`, which is populated by whichever provider completes login. To branch on the provider in backend code, use `await User.current_provider()`: ```python import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.auth import User class DemoState(rx.State): @rxe.event async def show_provider(self): provider = await User.current_provider() return rx.toast(provider.__provider__ if provider else "anonymous") ``` ## The claims a provider returns `OIDCUserInfo` is a `TypedDict` (`total=False`) declaring only `sub`. The OIDC spec requires that claim. Profile claims such as `name`, `email`, and `picture` appear only when their scope is granted. At runtime, `OIDCUserInfo` is a plain dict; read claims with `.get(...)`. To document the extra claims a provider returns, declare a nested `UserInfo(OIDCUserInfo, total=False)` on your provider: ```python from reflex_enterprise.auth import OIDCAuthState, OIDCUserInfo class OktaAuthState(OIDCAuthState, rx.State): __provider__ = "okta" class UserInfo(OIDCUserInfo, total=False): name: str email: str picture: str groups: list[str] ``` The common claims are projected as read-only Vars on `User` (`User.name`, `.email`, `.sub`, `.picture`); any other claim is read from the dict via `await User.current()` or `ctx.auth_user_state.userinfo.get(...)`. ## Advanced extension points `OIDCAuthState` exposes overridable async hooks for provider-specific behavior. Override only the hooks required by your provider subclass: | Hook | Purpose | | --- | --- | | `_validate_tokens(self) -> bool` | Validate the current access and ID tokens; return whether they're valid. | | `_verify_jwt(self, token_json) -> Token` | Verify the ID token JWT; override to customize verification. | | `_valid_issuers(self) -> list[str] \| None` | Acceptable `iss` claim values; override for e.g. Azure multi-tenant. | | `_set_tokens(self, access_token, id_token=None, refresh_token=None, granted_scopes=None, **kwargs)` | Persist tokens after exchange; override to handle extra response data. | | `_set_tokens_payload_from_exchange(self, exchange) -> dict` | Build the kwargs passed to `_set_tokens`; override to forward an extra field from the token-exchange response. | | `_validate_auth_callback_exchange(self, exchange) -> dict \| None` | Validate the token-exchange response from the callback. | | `_fetch_userinfo(self) -> OIDCUserInfo` | Fetch claims from the IdP's userinfo endpoint; override to fetch or reshape claims. | | `_redirect_to_login_payload(self) -> dict` | Build the authorization-request query params (scope, state, PKCE challenge); override for non-standard login params. | | `_redirect_to_logout_payload(self) -> dict[str, str]` | Build the IdP end-session params (`state`, `id_token_hint`, `post_logout_redirect_uri`); override for a custom `post_logout_redirect_uri` or non-standard end-session. | | `_on_access_token_change(self, new_access_token, refresh=False)` | React when the access token is set or refreshed. | | `_on_refresh_access_token(self, new_access_token)` | React specifically when the access token is refreshed. | ```md alert info # Logout behavior Logout chains the provider's discovered `end_session_endpoint` with an `id_token_hint` and `post_logout_redirect_uri`. If the IdP advertises no `end_session_endpoint`, logout clears local tokens and redirects to the app index. The IdP session remains active and a later login may reuse it. ``` ## Using the access token to call an API Inside an `@rx.event` (or computed var) on **your** `OIDCAuthState` subclass, `await self._access_token` to get the current OAuth access token, then send it as a bearer token to a downstream service or the IdP. The leading underscore does not indicate direct field access here: `_access_token` is a server-only awaitable Var, never exposed to the browser, and kept fresh by background refresh. ```python import httpx import reflex as rx from reflex_enterprise.auth import OIDCAuthState class OktaAuthState(OIDCAuthState, rx.State): __provider__ = "okta" @rx.event async def fetch_profile(self): access_token = await self._access_token async with httpx.AsyncClient() as client: resp = await client.get( "https://api.example.com/me", headers={"Authorization": f"Bearer {access_token}"}, ) resp.raise_for_status() ``` To react when the token is issued or refreshed, override `_on_access_token_change` or `_on_refresh_access_token` on the subclass. ## Sharing behavior across providers To put the same fields, vars, event handlers, or hook overrides on more than one provider without duplication, define an `rx.State` mixin with `mixin=True` and list it **before** `OIDCAuthState` in each provider's bases: ```python import reflex as rx from reflex_enterprise.auth import OIDCAuthState class SharedAuthBehavior(rx.State, mixin=True): async def _on_access_token_change( self, new_access_token, refresh=False ): ... # runs for every provider that mixes this in class OktaAuthState(SharedAuthBehavior, OIDCAuthState, rx.State): __provider__ = "okta" class AzureAuthState(SharedAuthBehavior, OIDCAuthState, rx.State): __provider__ = "azure" ``` Base order matters: the mixin must come before `OIDCAuthState`/`rx.State`. Each provider reads its own `__provider__`-namespaced cookies. The shared code operates on that provider's tokens. ## Persisting extra token-exchange data `_set_tokens_payload_from_exchange` builds the payload (`access_token`, plus any of `id_token`, `refresh_token`, and `granted_scopes` the exchange returned) from the IdP's token-exchange response, and that payload is the entire set of kwargs `_set_tokens` receives on both the callback and refresh paths. To persist a custom field from the exchange, override **both** hooks: `_set_tokens_payload_from_exchange` carries the field through, and `_set_tokens` accepts and stores it. ```python import reflex as rx from reflex_enterprise.auth import OIDCAuthState class OrgAuthState(OIDCAuthState, rx.State): __provider__ = "myorg" _org_id: str = "" async def _set_tokens_payload_from_exchange(self, exchange): payload = await super()._set_tokens_payload_from_exchange(exchange) if "org_id" in exchange: payload["org_id"] = exchange["org_id"] return payload async def _set_tokens( self, access_token, id_token=None, refresh_token=None, granted_scopes=None, org_id="", **kwargs, ): await super()._set_tokens( access_token, id_token, refresh_token, granted_scopes, **kwargs ) self._org_id = org_id ``` Under the popup/iframe flow, the popup and opener are separate clients. The popup runs the callback and captures the field in its own state, then posts the tokens to the opener, which applies them through `on_iframe_auth_success`, not `_set_tokens_payload_from_exchange`. Only the posted tokens cross over, so a custom field stored on the popup's `self` does not automatically reach the opener. Give the extra `_set_tokens` keyword a default (as `org_id=""` above) so the opener's call works. ## Migrating from `register_auth_endpoints` `OIDCAuthState.register_auth_endpoints(app)` is deprecated (since reflex-enterprise v0.9.1, removed in 1.0). Register `rxe.AuthPlugin` in `rxe.Config(plugins=[...])` instead. The plugin registers `/login`, `/logout`, `/callback`, and `/forbidden`, and applies the secure-by-default protections. ## Related - [Secure by default](/docs/enterprise/auth/secure-by-default/): protected surfaces and authorization checks. - [Custom pages](/docs/enterprise/auth/custom-pages/): custom login, callback, logout, and forbidden page builders. - [Testing](/docs/enterprise/auth/testing/): guarded surfaces and mock-IdP tests. # Secure by Default Source: http://localhost:3000/docs/enterprise/auth/secure-by-default.md --- title: Secure by Default --- _New in reflex-enterprise v0.9.1._ Once `rxe.AuthPlugin` is configured (see the [overview](/docs/enterprise/auth/overview/)), **every** non-exempt page, event handler, base field, and computed var in your app requires a logged-in user unless you explicitly opt it out. ```md alert warning # Requires `rxe.App` and a configured provider Secure-by-default only applies when your app uses `rxe.App()` (not `rx.App()`) and `rxe.AuthPlugin` is in `rxe.Config(plugins=[...])` with an OIDC provider configured. See the [overview](/docs/enterprise/auth/overview/). ``` ## The `auth=` value `auth=` accepts three kinds of value: | `auth=` value | Meaning | | --- | --- | | `True` | Require an authenticated user. This is the default for every surface. | | `False` | Public. Allow everyone. | | a callable check | An authorization check that runs **only after** authentication succeeds. A truthy result allows; a falsey result or a raised exception denies. | The four wrappers are exported at top level: `rxe.page`, `rxe.event`, `rxe.field`, and `rxe.var`. `rxe.event`, `rxe.field`, and `rxe.var` accept all three values. `rxe.page` takes a **bool only**. ### The global default `AuthPlugin(auth=...)` sets the default applied to every surface that carries no explicit `auth=` of its own. It accepts the same three values: ```python rxe.AuthPlugin(auth=True) # any authenticated user (the default) rxe.AuthPlugin(auth=False) # open by default rxe.AuthPlugin(auth=my_org_check) # org-wide authorization check after login ``` A plain `rx.field(...)`, a bare `@rxe.var`, a bare `@rxe.event`, and an `@rxe.page()` with no `auth=` all **inherit** this global default. An explicit per-surface `rxe.*(auth=...)` always overrides it. Changing `AuthPlugin(auth=...)` changes the app baseline without editing each field, var, event, or page. ### Gate the whole app behind a role A callable global default runs on every untagged page load: ```python # my_app/auth.py from reflex_enterprise.auth import AuthContext def is_member_of(ctx: AuthContext) -> bool: """Allow only members of the ``admins`` group.""" return "admins" in (ctx.auth_user_state.userinfo.get("groups") or []) ``` ```python # rxconfig.py import reflex_enterprise as rxe from my_app.auth import is_member_of config = rxe.Config( app_name="my_app", plugins=[ rxe.AuthPlugin( auth=is_member_of, extra_scopes=["groups"], ) ], ) ``` Request `extra_scopes=["groups"]` when checks need the `groups` claim. See [providers](/docs/enterprise/auth/providers/#the-claims-a-provider-returns). Authenticated users who fail the check are sent to `/forbidden`, not back to login. Replace that screen with a [custom forbidden page](/docs/enterprise/auth/custom-pages/#a-custom-forbidden-page). ## Pages Protect a page with `@rxe.page`. For pages, `auth` is a **bool only**. Callable checks are not supported as a per-page argument, although the global default may still be callable. ```python def page( route: str | None = None, *, auth: bool | None = None, **page_kwargs, ) ``` - `auth=None` (the default): follow the configured global default (`AuthPlugin(auth=...)`). - `auth=True`: always require an authenticated user, regardless of the global default. - `auth=False`: public page. A protected page injects a guard as the **first** `on_load` event. Anonymous visitors are redirected to the login endpoint, and the requested page is preserved as a `redirect_to` query parameter. The post-login flow returns them there. - The post-login `redirect_to` target is validated against the app's origin. Only same-origin absolute paths (e.g. `/dashboard`) or URLs sharing the app's scheme and host are honored. Any cross-origin, scheme-relative (`//evil.test`), or backslash target falls back to the index page. ```python @rxe.page( route="/dashboard", title="Dashboard" ) # auth=None: follows the global default def dashboard() -> rx.Component: """Protected: anonymous visitors are redirected to /login.""" ... @rxe.page(route="/", title="Home", auth=False) def index() -> rx.Component: """Public landing page (opted out of secure-by-default).""" ... ``` Any extra `**page_kwargs` are forwarded to `rx.page`: `title`, `image`, `description`, `meta`, `script_tags`, and `on_load`. When `on_load` is provided, the guard is prepended. ### Reading the user on page load Because the guard is prepended, a page's `on_load` runs after authentication. `await User.current()` (or `ctx.auth_user_state.userinfo`) is non-`None` there: ```python @rxe.page(route="/dashboard", title="Dashboard", on_load=DashboardState.load) def dashboard() -> rx.Component: ... class DashboardState(rx.State): greeting: str = "" @rxe.event async def load(self): user = await User.current() or {} self.greeting = f"Welcome, {user.get('name') or user.get('sub')}" ``` `User.current()` is event-only; an `on_load` handler is an event and resolves the authenticated user from the guard. See [reading the current user](#reading-the-current-user). ### Pages Added Without `@rxe.page` With the plugin active, `rxe.App()` defaults **every** page to login-required, including pages added via `app.add_page(...)` or a plain `@rx.page`. Opt out with `auth=False`: ```python app.add_page(index, route="/", auth=False) ``` Because plain `@rx.page` takes no `auth` argument, use `@rxe.page(auth=False)` to opt a decorated page out. ### When the global default is a callable A page cannot take a callable `auth=` directly. If `AuthPlugin(auth=...)` is callable, untagged pages (`@rxe.page()` / `@rx.page` / `app.add_page`) run it on load. An authenticated visitor who **fails** the check is redirected to the `forbidden_endpoint` (`/forbidden` by default). An explicit `@rxe.page(auth=True)` only requires login and does **not** run the callable default. ## Event handlers Protect an event handler with `@rxe.event`. Here `auth` accepts a bool or a callable check. ```python def event( fn=None, *, auth: bool | EventAuthCheck = ..., **event_kwargs, ) ``` Works bare (`@rxe.event`) or called (`@rxe.event(auth=...)`), and can wrap a raw function or an already-converted `EventHandler`. Extra `**event_kwargs` are forwarded to `rx.event`: `background`, `stop_propagation`, `prevent_default`, `throttle`, `debounce`, and `temporal`. ```python class DemoState(rx.State): @rxe.event # default auth=True: anonymous callers are redirected to /login async def protected_action(self): user = await User.current() or {} return rx.toast(f"Hello {user.get('name') or user.get('sub')}!") @rxe.event(auth=False) def public_action(self): """Anyone may call this.""" ... ``` A failed authorization check on an event handler shows the `"Action not allowed"` toast (see [authentication vs authorization](#authentication-vs-authorization)). ## Base fields Base (state) fields are protected by default. A plain `rx.field(...)`, or a bare annotation, on one of your state classes is protected and dropped from the state delta until the user is resolved. Use `rxe.field` to opt a field out or attach a check. ```python def field( default=..., *, auth: bool | FieldVarAuthCheck = ..., default_factory=None, is_var=True, ) ``` ```python class DemoState(rx.State): # Protected by default; dropped from the delta until login. notes: rx.Field[str] = rx.field("These notes are only sent once you log in.") # Public; always sent to the client. loading: rx.Field[bool] = rxe.field(False, auth=False) ``` A withheld field is replaced in the delta with its **declared default** (the value baked into the frontend bundle) until the user is resolved. ## Computed vars Computed vars are protected by default and withheld until login. Wrap them with `@rxe.var`. ```python def var( fget=None, *, auth: bool | FieldVarAuthCheck = ..., **var_kwargs, ) ``` Usable bare (`@rxe.var`) or called (`@rxe.var(auth=..., initial_value=...)`). Extra `**var_kwargs` are forwarded verbatim to `rx.var`: `initial_value`, `cache`, `deps`, `auto_deps`, `interval`, and `backend`. ```md alert info # Always pair a protected var with `initial_value` A protected var is withheld until the user is resolved. Set `initial_value=` to a placeholder baked into the frontend bundle and shown until the real value is delivered after login. ``` ```python class DemoState(rx.State): @rxe.var(initial_value="🔒 log in to reveal this") def protected_tip(self) -> str: """Protected by default; the placeholder shows until a user is resolved.""" return "✅ Delivered only to logged-in users." @rxe.var(auth=False) def public_label(self) -> str: """Opened up to anonymous visitors.""" return "Anyone can read this." ``` ## Authorization checks For finer-grained control than "any authenticated user," attach a **callable** `auth=` check to the specific events, vars, or fields that need it. A check is a function that receives a single **context object** and returns a bool (or an awaitable of one): ```python def check(ctx) -> bool: ... # sync async def check(ctx) -> bool: ... # async ``` A check runs **only after** authentication succeeds. Anonymous callers are redirected to login before the check runs, and a resolved user is always present inside the check. ### The context object Each surface passes a different context, all carrying the current user as `ctx.auth_user_state` (an `AuthUserState`). Import them from `reflex_enterprise.auth`: | Context | Surface | Extra attributes | | --- | --- | --- | | `EventAuthContext` | event handler | `event_handler` (the gated handler), `payload` (the event payload dict) | | `VarAuthContext` | field / computed var | `field_or_var` (the `Var`, or `None`) | | `PageAuthContext` | page (callable global default only) | None | Read the user's claims from `ctx.auth_user_state.userinfo`, a plain dict: ```python from reflex_enterprise.auth import EventAuthContext, VarAuthContext def is_admin(ctx: EventAuthContext) -> bool: """Event check: allow only members of the ``admins`` group.""" return "admins" in (ctx.auth_user_state.userinfo.get("groups") or []) def is_staff(ctx: VarAuthContext) -> bool: """Field/var check: allow only staff.""" return "staff" in (ctx.auth_user_state.userinfo.get("groups") or []) class DemoState(rx.State): @rxe.event(auth=is_admin) # authorization failure -> "Action not allowed" toast def admin_action(self): return rx.toast.success("Admin action executed.") # Withheld unless the staff check passes; the placeholder shows otherwise. audit_log: rx.Field[list[str]] = rxe.field([], auth=is_staff) @rxe.var(auth=is_staff, initial_value="🔒 staff only") def staff_view(self) -> str: return "✅ staff-only computed var." ``` ### One check for any surface Annotate `ctx` with the `AuthContext` union to make one function usable on **any** surface; annotate it with a single context type to restrict it (and get exact autocomplete). `isinstance(ctx, EventAuthContext)` narrows the union inside the body: ```python from reflex_enterprise.auth import AuthContext, EventAuthContext def is_admin(ctx: AuthContext) -> bool: """Usable as an event, field, var, or global-default check.""" return "admins" in (ctx.auth_user_state.userinfo.get("groups") or []) ``` ```md alert warning # Cached claims can lag the IdP by up to 30 minutes `ctx.auth_user_state.userinfo` is served from a server-side cache refreshed at most every 30 minutes. The access token is re-read at most about every 60 seconds. Checks that depend on `groups` or roles may see IdP-side changes, such as group revocation, up to 30 minutes late within one token's life. The cache is invalidated whenever the access token changes, so short-lived-token setups using a refresh token (`extra_scopes=["offline_access"]`) pick up changes sooner. If revocation must take effect immediately, check your database or authorization service in an async check instead of relying on cached claims. ``` ### Async checks Most checks can be sync. Read OIDC claims from `ctx.auth_user_state.userinfo`: ```python import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.auth import AuthContext def is_org_admin(ctx: AuthContext) -> bool: return "admins" in (ctx.auth_user_state.userinfo.get("groups") or []) class DemoState(rx.State): @rxe.event(auth=is_org_admin) async def privileged_action(self): return rx.toast.success("Done.") @rxe.var(auth=is_org_admin, initial_value="🔒 admins only") def admin_view(self) -> str: return "✅ admin-only value." ``` Request `extra_scopes=["groups"]` during OAuth login when checks depend on the `groups` claim. A check may also be async. The framework awaits it at the right point: the per-event gate for handlers, or delta resolution for fields and vars. Use an async check when it calls async APIs, for example: - Querying a database. - Calling a remote authorization service, such as OpenFGA or another ReBAC backend. - Accessing another Reflex state with `await ctx.auth_user_state.get_state(...)` when the policy input is stored in state. ```md alert warning # One function, one auth value The same function cannot back two surfaces with different `auth` values, such as one var with `auth=True` and another with `auth=False` sharing a single getter. That raises `ValueError`. Reusing a function with the same auth value is supported; otherwise define a separate function per surface. ``` ## Authentication vs authorization The two failure modes are deliberately different. Applied per surface against the resolved user: | Situation | Outcome | | --- | --- | | `auth=False` | **Allow.** | | Not logged in (no user resolved) | **Authentication failure**. Handled before any check runs. | | Logged in and `auth=True` | **Allow.** | | Logged in and the check returns truthy | **Allow.** | | Logged in and the check returns falsey or raises | **Authorization failure.** Never a login redirect. | What each failure does depends on the surface: | Surface | Authentication failure (anonymous) | Authorization failure (check said no) | | --- | --- | --- | | Event handler | block + redirect to `/login` | block + `"Action not allowed"` toast | | Page | redirect to `/login` (with `redirect_to`) | redirect to `/forbidden` | | Field / computed var | withheld (placeholder / default shown) | withheld (placeholder / default shown) | Two properties follow from the ordering: a check **never runs for an anonymous caller**, and a check that **raises fails closed**. Exceptions are treated as deny results. ## How withholding works Protected base fields are dropped from the state delta, and protected computed vars are withheld, for any caller who isn't authorized to see them. A sync check is evaluated inline as the delta is built; an async check is deferred and awaited during delta resolution. The `hydrate` event runs **before** the auth cookies are known. Even for a logged-in user, protected values are withheld at first because the user has not been resolved yet. Once an event resolves an authenticated user (for example, the page guard on a protected page), the protected names are re-delivered in that event's delta, filtered against the resolved user. Set `initial_value` on protected computed vars. The placeholder is baked into the frontend bundle and shown until the real value arrives after login. ## Logout resets protected state On logout, each non-exempt state's **protected** surface is reset. This prevents one user's session data from leaking to the next user on the same client token: - Protected base vars revert to their declared defaults. - Protected cached computed vars are dropped. - Server-only backend vars are cleared. **Public (`auth=False`) fields and vars are preserved** across logout. They are not part of the authenticated session. ### Logout is protected against CSRF The plugin installs middleware for the configured `logout_endpoint`. Cross-site GET navigations to `/logout` are blocked when the browser sends `Sec-Fetch-Site: cross-site`; those requests are redirected to the frontend root. Same-origin logout requests continue normally. No configuration is required. ```md alert warning # Only covers the backend-served frontend This guard applies only when the backend serves the compiled frontend, which is the default fullstack deployment. In a split frontend/backend deployment, the logout page is served elsewhere and is **not** protected. Add a cross-site guard there. ``` ## Exempt states Some state classes are never protected and never gated: - State classes defined inside `reflex` or `reflex_enterprise`. - Any `OIDCAuthState` subclass, including user-defined auth providers. These exemptions let provider states read auth cookies before login and let the page guard resolve the current user without being gated. ## Reading the current user Import the `User` facade to read the current user from either the frontend or the backend: ```python from reflex_enterprise.auth import User ``` `User` is an alias of `reflex_enterprise.auth.AuthUserState` and may be used interchangeably. **Frontend Vars**: embed these class-level descriptors directly in components. They bind to `AuthUserState`, populated after login by the provider that authenticated the user. Each is typed `str` and is empty (`""`) until login: | Attribute | Value | | --- | --- | | `User.name` | The user's name claim. | | `User.email` | The user's email claim. | | `User.sub` | The user's subject identifier. | | `User.picture` | The user's picture URL. | ```python rx.avatar(src=User.picture, fallback="U", size="5") rx.heading(User.name, size="6") rx.text(User.email, color_scheme="gray") rx.cond(User.sub != "", rx.text("Signed in"), rx.link("Log in", href="/login")) ``` ### Reading other claims in a component The common OIDC claims `name`, `email`, `sub`, and `picture` are projected as frontend Vars. The full `userinfo` dict is server-only and never serialized. To render any other claim (`groups`, roles, a custom field) in a component, opt it onto the frontend by declaring a computed var on an `AuthUserState` substate: ```python import reflex as rx from reflex_enterprise.auth import AuthUserState class UserExtras(AuthUserState): @rx.var def groups(self) -> list[str]: return self.userinfo.get("groups") or [] # in a component: rx.cond(UserExtras.groups.contains("admins"), rx.badge("Admin"), rx.fragment()) ``` For backend checks, read the same claims via `await User.current()` or `ctx.auth_user_state.userinfo.get(...)`. See [the claims a provider returns](/docs/enterprise/auth/providers/#the-claims-a-provider-returns). **Backend**: use these inside an event handler. `current()` and `current_provider()` are async: | Call | Returns | | --- | --- | | `await User.current()` | The current user's `OIDCUserInfo` claims dict for this event, or `None` when anonymous. | | `await User.current_provider()` | The provider **class** that actually authenticated this event's user, or `None`. Correct in multi-provider setups. | | `User.logout` | Event handler that signs the current user out. Bind it (`on_click=User.logout`) or return it from a handler. Redirects to the home page (`/`) when anonymous. | Inside an authorization check, `ctx.auth_user_state.provider` returns the same provider class. `OIDCUserInfo` is a plain dict at runtime. Read claims with `.get(...)`: ```python class DemoState(rx.State): @rxe.event # default auth=True async def whoami(self): user = await User.current() or {} return rx.toast(f"You are {user.get('name') or user.get('sub')}.") ``` ## Related - [Overview](/docs/enterprise/auth/overview/): plugin setup and the login flow. - [Providers](/docs/enterprise/auth/providers/): provider configuration. - [Custom pages](/docs/enterprise/auth/custom-pages/): custom login, callback, logout, and forbidden pages. - [Testing](/docs/enterprise/auth/testing/): unit tests and mock-IdP flow tests. # Testing Guarded Code Source: http://localhost:3000/docs/enterprise/auth/testing.md --- title: Testing Guarded Code --- _New in reflex-enterprise v0.9.1._ When the [AuthPlugin](/docs/enterprise/auth/overview/) is enabled, every non-exempt page, event handler, base field, and computed var is [secure by default](/docs/enterprise/auth/secure-by-default/). The logic worth testing is usually the `auth=` authorization checks that decide who may see a value or run a handler. Because a check is an ordinary function that takes a context object and returns a bool, you can test it directly with no network, IdP, or browser. Use a local mock provider when the OIDC wiring itself is under test. ```md alert info # Run these under pytest The snippets construct `AuthUserState()` directly, which Reflex permits only in a pytest environment. Run them with `pytest`, not as standalone scripts. Async-check tests use [`pytest-asyncio`](https://pypi.org/project/pytest-asyncio/) (`uv add --dev pytest-asyncio`); the `@pytest.mark.asyncio` marker shown below works whether or not your project sets `asyncio_mode = "auto"` in `pyproject.toml`. ``` ## A check is a function An authorization check receives a single context object and returns a bool (or an awaitable of one). It reads the user's claims from `ctx.auth_user_state.userinfo`: ```python from reflex_enterprise.auth import VarAuthContext def is_staff(ctx: VarAuthContext) -> bool: return "staff" in (ctx.auth_user_state.userinfo.get("groups") or []) ``` To test it, build a context around an `AuthUserState` carrying the claims for that case, and call the check. Set the claims on the private `_userinfo` attribute; the check reads them back through the public `userinfo` property: ```python from reflex_enterprise.auth import AuthUserState, VarAuthContext from my_app.auth import is_staff def _ctx(userinfo) -> VarAuthContext: """A VarAuthContext presenting ``userinfo`` as the current user.""" user = AuthUserState() user._userinfo = userinfo return VarAuthContext(auth_user_state=user, field_or_var=None) def test_is_staff_allows_member(): assert is_staff(_ctx({"sub": "u1", "groups": ["staff"]})) is True def test_is_staff_denies_non_member(): assert is_staff(_ctx({"sub": "u2", "groups": ["guests"]})) is False ``` The context classes are exported from `reflex_enterprise.auth`. Build the one that matches the guarded surface: | Context | Construct as | | --- | --- | | `VarAuthContext` | `VarAuthContext(auth_user_state=user, field_or_var=None)` | | `EventAuthContext` | `EventAuthContext(auth_user_state=user, event_handler=None, payload={})` | | `PageAuthContext` | `PageAuthContext(auth_user_state=user)` | A check typed with the `AuthContext` union works on any surface. Test it through the simplest matching context, usually `VarAuthContext`. ```md alert info # A check never runs for an anonymous caller The plugin resolves authentication before any check. `ctx.auth_user_state` is always a logged-in user inside a check. Test anonymous behavior at the protected surface level, not in the check function. ``` ## Async checks Async checks are tested the same way. Pass the claims into the test context and await the check: ```python import pytest @pytest.mark.asyncio async def test_async_check(): assert await my_async_check(_ctx({"sub": "u1", "groups": ["admins"]})) is True ``` Use the mock IdP flow below when the OIDC wiring itself or live Reflex state is under test. ## End-to-end against a mock IdP To exercise the OIDC flow, including the login redirect, `/callback` token exchange, JWKS validation, userinfo fetch, and async checks that touch real state, run the app against a local mock identity provider. [`oidc-provider-mock`](https://pypi.org/project/oidc-provider-mock/) is a small OIDC server that runs in-process. Add it as a dev dependency: ```bash uv add --dev oidc-provider-mock ``` Run it on a background thread and point the `OIDC_*` env vars at it before the app starts. It accepts any client credentials by default and issues refresh tokens: ```python import os import pytest from oidc_provider_mock import User, run_server_in_thread @pytest.fixture(scope="session") def mock_idp(): """Run a local mock OIDC IdP and point the OIDC_* env vars at it.""" env = { # Lets the mock IdP (authlib, server-side) serve OAuth over plain HTTP. # Redundant for a localhost issuer, needed for a non-localhost HTTP host. "AUTHLIB_INSECURE_TRANSPORT": "1", "OIDC_CLIENT_ID": "test-client", "OIDC_CLIENT_SECRET": "test-secret", } # Save and restore to avoid leaking OIDC_* into other tests. saved = {key: os.environ.get(key) for key in [*env, "OIDC_ISSUER_URI"]} os.environ.update(env) users = [ User(sub="user-1", claims={"name": "Ada Lovelace", "groups": ["admins"]}), ] try: with run_server_in_thread(user_claims=users) as server: os.environ["OIDC_ISSUER_URI"] = f"http://localhost:{server.server_port}" yield server finally: for key, value in saved.items(): if value is None: os.environ.pop(key, None) else: os.environ[key] = value ``` The login flow is browser-driven: redirects, cookies, and websocket state. Exercise it with `AppHarness` (from `reflex.testing`) and a browser driver such as Playwright. With the `mock_idp` fixture above active, drive the auth-specific steps and assert that a protected value is delivered: ```python import re from playwright.sync_api import Page, expect def test_login_delivers_protected_value(auth_app, page: Page): base = auth_app.frontend_url.rstrip("/") # A protected page bounces an anonymous visitor to /login. page.goto(f"{base}/dashboard") page.wait_for_url(re.compile(r"/login")) # Click the app's login button (labelled "Login with {display_name()}"). # This starts the OIDC redirect to the IdP's authorize page. page.get_by_role("button", name="Login with Generic").click() # Authorize on the IdP. oidc-provider-mock authorizes with one passwordless # click per predefined user; a real IdP shows its own login/consent form here. page.locator('button[name="sub"][value="user-1"]').click() # Control returns to the app; the callback exchanges tokens and the # protected value is now delivered. page.wait_for_url(lambda url: url.startswith(base) and "callback" not in url) expect(page.get_by_text("Ada Lovelace")).to_be_visible() ``` The `auth_app` fixture starts your app under `AppHarness` with `mock_idp` active; `page` is the standard Playwright fixture. `oidc-provider-mock` also ships a CLI for standalone manual testing. ```md alert info # Test the check directly first; the mock IdP when you're testing the wiring Calling the check with a hand-built context is faster and needs no server. Use `oidc-provider-mock` when the OIDC wiring itself (redirect, callback, token exchange, refresh) or an async check that touches live state is under test. ``` ## Related - [Secure by default](/docs/enterprise/auth/secure-by-default/): `auth=`, context objects, and enforcement semantics. - [Overview](/docs/enterprise/auth/overview/): plugin setup and current-user access. - [Providers](/docs/enterprise/auth/providers/): provider configuration. # Built with Reflex Badge Source: http://localhost:3000/docs/enterprise/built-with-reflex.md The "Built with Reflex" badge appears in the bottom right corner of apps using reflex-enterprise components. ## Removing the Badge To remove the badge, you need to be on the Enterprise tier. ## Configuration ```python import reflex_enterprise as rxe config = rxe.Config( show_built_with_reflex=False, # Requires paid tier ) ``` # Components Source: http://localhost:3000/docs/enterprise/components.md --- title: Enterprise Components --- ```python exec import reflex as rx def enterprise_component_grid(): sections = [ { "title": "AG Grid", "description": "Advanced data grid with sorting, filtering, editing, and pagination", "link": "/docs/enterprise/ag-grid/", "components": [ ("Overview", "/docs/enterprise/ag-grid/"), ("Column Definitions", "/docs/enterprise/ag-grid/column-defs/"), ("Aligned Grids", "/docs/enterprise/ag-grid/aligned-grids/"), ("Model Wrapper", "/docs/enterprise/ag-grid/model-wrapper/"), ("Pivot Mode", "/docs/enterprise/ag-grid/pivot-mode/"), ("Theme", "/docs/enterprise/ag-grid/theme/"), ("Value Transformers", "/docs/enterprise/ag-grid/value-transformers/"), ], }, { "title": "AG Chart", "description": "Interactive charts and data visualization", "link": "/docs/enterprise/ag-chart/", "components": [ ("Overview", "/docs/enterprise/ag-chart/"), ], }, { "title": "Interactive Components", "description": "Drag-and-drop and mapping functionality", "link": "/docs/enterprise/drag-and-drop/", "components": [ ("Drag and Drop", "/docs/enterprise/drag-and-drop/"), ("Mapping", "/docs/enterprise/map/"), ], }, { "title": "Mantine", "description": "Rich UI components from Mantine library", "link": "/docs/enterprise/mantine/", "components": [ ("Overview", "/docs/enterprise/mantine/"), ("Autocomplete", "/docs/enterprise/mantine/autocomplete/"), ("Collapse", "/docs/enterprise/mantine/collapse/"), ("Combobox", "/docs/enterprise/mantine/combobox/"), ("JSON Input", "/docs/enterprise/mantine/json-input/"), ("Loading Overlay", "/docs/enterprise/mantine/loading-overlay/"), ("Multi Select", "/docs/enterprise/mantine/multi-select/"), ("Number Formatter", "/docs/enterprise/mantine/number-formatter/"), ("Pill", "/docs/enterprise/mantine/pill/"), ("Ring Progress", "/docs/enterprise/mantine/ring-progress/"), ( "Semi Circle Progress", "/docs/enterprise/mantine/semi-circle-progress/", ), ("Spoiler", "/docs/enterprise/mantine/spoiler/"), ("Tags Input", "/docs/enterprise/mantine/tags-input/"), ("Timeline", "/docs/enterprise/mantine/timeline/"), ("Tree", "/docs/enterprise/mantine/tree/"), ], }, ] cards = [] for section in sections: cards.append( rx.box( rx.link( rx.el.h2( section["title"], class_name="font-large text-secondary-12", ), rx.icon("arrow_up_right", size=16, class_name="text-secondary-11"), href=section["link"], underline="none", class_name="px-4 py-2 bg-secondary-1 hover:bg-secondary-3 transition-bg flex flex-row justify-between items-center !text-secondary-12", ), rx.text( section["description"], class_name="px-4 py-2 font-small text-secondary-9 border-t border-secondary-5", ), rx.box( *[ rx.link( comp[0], href=comp[1], class_name="font-small text-secondary-11 hover:!text-primary-9 transition-color w-fit", ) for comp in section["components"] ], class_name="flex flex-col gap-2.5 px-4 py-2 border-t border-secondary-5", ), class_name="flex flex-col border border-secondary-5 rounded-xl bg-secondary-2 shadow-large overflow-hidden", ) ) return rx.box( *cards, class_name="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8", ) component_grid = enterprise_component_grid() ``` ```python eval rx.el.h1( "Enterprise Components", class_name="lg:text-5xl text-3xl font-[525] scroll-mt-[113px] my-4 text-secondary-12", ) ``` ```python eval rx.el.span( "Advanced UI components and features to enhance your Reflex applications. Available for free with the 'Built with Reflex' badge, or without the badge with an enterprise license.", class_name="font-[475] text-secondary-11 max-w-[80%] text-sm", ) ``` ```python eval component_grid ``` # Drag and Drop Source: http://localhost:3000/docs/enterprise/drag-and-drop.md --- title: Drag and Drop --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` Reflex Enterprise provides comprehensive drag and drop functionality for creating interactive UI elements using the `rxe.dnd` module. Built on top of react-dnd, it offers both high-level components for common use cases and low-level hooks for advanced scenarios. ```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 ### Simple Drag and Drop Here's a basic example showing how to create a draggable item and drop target: ```python demo exec import reflex as rx import reflex_enterprise as rxe class BasicDndState(rx.State): drop_count: int = 0 def increment_drop_count(self): self.drop_count += 1 @rx.memo def draggable_card() -> rxe.dnd.Draggable: return rxe.dnd.draggable( rx.card( rx.text("Drag me!", weight="bold"), rx.text("I can be moved around"), bg="blue.500", color="white", p=4, cursor="grab", width="200px", height="100px", ), type="BasicCard", item={"message": "Hello from draggable!"}, ) def basic_drag_drop(): return rx.vstack( rx.text(f"Items dropped: {BasicDndState.drop_count}"), rx.hstack( draggable_card(), rxe.dnd.drop_target( rx.box( "Drop Zone", bg="gray.100", border="2px dashed gray", min_height="150px", min_width="200px", display="flex", align_items="center", justify_content="center", font_weight="bold", ), accept=["BasicCard"], on_drop=BasicDndState.increment_drop_count, ), spacing="4", align="start", ), spacing="4", ) ``` ### Multi-Position Drag and Drop Create a draggable item that can be moved between multiple drop targets: ```python demo exec import reflex as rx import reflex_enterprise as rxe class MultiPositionState(rx.State): card_position: int = 0 def set_card_position(self, position: int): self.card_position = position @rx.memo def movable_card() -> rxe.dnd.Draggable: return rxe.dnd.draggable( rx.card( rx.text("Movable Card", weight="bold"), rx.text("Position: " + MultiPositionState.card_position.to_string()), bg="purple.500", color="white", p=4, width="180px", height="120px", ), type="MovableCard", border="2px solid purple", ) def drop_zone(position: int): params = rxe.dnd.DropTarget.collected_params return rxe.dnd.drop_target( rx.cond( MultiPositionState.card_position == position, movable_card(), rx.box(f"Drop Zone {position}", color="gray.600", font_weight="bold"), ), width="200px", height="200px", border="2px solid red", border_color=rx.cond(params.is_over, "green.500", "red.500"), bg=rx.cond(params.is_over, "green.100", "blue.100"), accept=["MovableCard"], on_drop=lambda _: MultiPositionState.set_card_position(position), display="flex", align_items="center", justify_content="center", ) def multi_position_example(): return rx.vstack( rx.text("Drag the card between positions", weight="bold"), rx.grid( drop_zone(0), drop_zone(1), drop_zone(2), drop_zone(3), columns="2", spacing="4", ), spacing="4", ) ``` ## Advanced Features ### State Tracking with Collected Parameters Access drag and drop state information using collected parameters: ```python demo exec import reflex as rx import reflex_enterprise as rxe class StateTrackingState(rx.State): drag_info: str = "No drag activity" def set_drag_info(self, value: str): self.drag_info = value @rx.memo def tracked_draggable() -> rxe.dnd.Draggable: drag_params = rxe.dnd.Draggable.collected_params return rxe.dnd.draggable( rx.card( rx.text("Tracked Draggable"), rx.text(rx.cond(drag_params.is_dragging, "Dragging...", "Ready to drag")), bg=rx.cond(drag_params.is_dragging, "orange.500", "blue.500"), color="white", p=4, opacity=rx.cond(drag_params.is_dragging, 0.5, 1.0), ), type="TrackedItem", on_end=StateTrackingState.set_drag_info("Drag ended"), ) def tracked_drop_target(): drop_params = rxe.dnd.DropTarget.collected_params return rxe.dnd.drop_target( rx.box( rx.text("Smart Drop Zone"), rx.text(rx.cond(drop_params.is_over, "Ready to receive!", "Waiting...")), bg=rx.cond(drop_params.is_over, "green.200", "gray.100"), border=rx.cond(drop_params.is_over, "2px solid green", "2px dashed gray"), p=4, min_height="150px", display="flex", flex_direction="column", align_items="center", justify_content="center", ), accept=["TrackedItem"], on_drop=StateTrackingState.set_drag_info("Item successfully dropped!"), on_hover=StateTrackingState.set_drag_info("Item hovering over drop zone"), ) def state_tracking_example(): return rx.vstack( rx.text(f"Status: {StateTrackingState.drag_info}"), rx.hstack(tracked_draggable(), tracked_drop_target(), spacing="4"), spacing="4", ) ``` ### Dynamic Lists with Drag and Drop Create dynamic draggable lists using `rx.foreach`. Always pass a stable item identifier as the `key` prop to the outermost component rendered by the foreach to ensure item identity is trackable as the item in the underlying list is rearranged. ```python demo exec import dataclasses import reflex as rx import reflex_enterprise as rxe @dataclasses.dataclass class ListItem: id: str text: str list_id: str class DynamicListState(rx.State): list_a: list[ListItem] = [ ListItem(id="1", text="Item 1", list_id="A"), ListItem(id="2", text="Item 2", list_id="A"), ListItem(id="3", text="Item 3", list_id="A"), ] list_b: list[ListItem] = [ ListItem(id="4", text="Item 4", list_id="B"), ListItem(id="5", text="Item 5", list_id="B"), ] def move_item(self, item_data: dict, target_list: str): item_id = item_data.get("id") source_list = item_data.get("list_id") if not item_id or not source_list: return # Find the item in the source list source_items = getattr(self, f"list_{source_list.lower()}") item_to_move = None for item in source_items: if item.id == item_id: item_to_move = item break if not item_to_move: return # Remove from source list only if source_list == "A": self.list_a = [item for item in self.list_a if item.id != item_id] else: self.list_b = [item for item in self.list_b if item.id != item_id] # Create new item for target list new_item = ListItem(id=item_id, text=item_to_move.text, list_id=target_list) # Add to target list if target_list == "A": self.list_a.append(new_item) else: self.list_b.append(new_item) @rx.memo def draggable_list_item(item: rx.Var[ListItem]) -> rx.Component: return rxe.dnd.draggable( rx.card( rx.text(item.text, weight="bold"), rx.text(f"From List {item.list_id}", size="2", color="gray.600"), p=3, cursor="grab", _hover={"bg": "gray.50"}, ), type="ListItem", item={"id": item.id, "text": item.text, "list_id": item.list_id}, ) def droppable_list(title: str, items: list[ListItem], list_id: str): return rxe.dnd.drop_target( rx.vstack( rx.text(title, weight="bold", size="5"), rx.vstack( rx.foreach( items, lambda item, index: draggable_list_item(item=item, key=item.id), ), spacing="2", min_height="200px", width="100%", ), bg="gray.50", p=4, border_radius="md", border="2px dashed gray", width="250px", ), accept=["ListItem"], on_drop=lambda item: DynamicListState.move_item(item, list_id), ) def dynamic_list_example(): return rx.hstack( droppable_list("List A", DynamicListState.list_a, "A"), droppable_list("List B", DynamicListState.list_b, "B"), spacing="6", align="start", ) ``` ## Core Components ### Draggable The `rxe.dnd.draggable` component makes any element draggable: **Key Properties:** - `type`: String identifier for drag type matching - `item`: Data object passed to drop handlers - `on_end`: Called when drag operation ends 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: **Key Properties:** - `accept`: List of drag types this target accepts - `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: **Draggable Parameters (`rxe.dnd.Draggable.collected_params`):** - `is_dragging`: Boolean indicating if item is being dragged **Drop Target Parameters (`rxe.dnd.DropTarget.collected_params`):** - `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, ), 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 Creates a draggable component that can be moved around the interface. **Parameters:** - **`type`** (str, required): String identifier that must match the `accept` list of drop targets - **`item`** (dict | Callable): Data object passed to drop handlers. Can be a static dictionary or a function that receives a `DragSourceMonitor` and returns data - **`preview_options`** (dict): Configuration for the drag preview appearance - **`options`** (dict): Additional drag source options like `dropEffect` - **`on_end`** (EventHandler): Event handler called when drag operation completes - **`can_drag`** (Callable): Function that determines if the item can be dragged - **`is_dragging`** (Callable): Function to override the default dragging state detection - **`collect`** (Callable): Function to collect custom properties from the drag monitor ### rxe.dnd.drop_target Creates a drop target that can receive draggable items. **Parameters:** - **`accept`** (str | list[str], required): Drag type(s) this target accepts - **`options`** (dict): Additional drop target configuration options - **`on_drop`** (EventHandler): Event handler called when an item is dropped, receives the `item` data - **`on_hover`** (EventHandler): Event handler called when an item hovers over the target - **`can_drop`** (Callable): Function that determines if a specific item can be dropped - **`collect`** (Callable): Function to collect custom properties from the drop monitor ## Monitor Classes ### DragSourceMonitor Provides information about the drag operation state: - **`is_dragging()`**: Returns `True` if this item is currently being dragged - **`can_drag()`**: Returns `True` if the item can be dragged - **`get_item()`**: Returns the item data being dragged - **`get_item_type()`**: Returns the drag type string - **`get_drop_result()`**: Returns the drop result (available in `on_end`) - **`did_drop()`**: Returns `True` if the item was successfully dropped ### DropTargetMonitor Provides information about the drop target state: - **`is_over()`**: Returns `True` if a draggable item is hovering over this target - **`can_drop()`**: Returns `True` if the hovering item can be dropped - **`get_item()`**: Returns the item data of the hovering draggable - **`get_item_type()`**: Returns the drag type of the hovering item ## Default Collected Parameters ### Draggable.collected_params ```python { "is_dragging": bool, # True when this item is being dragged "can_drag": bool, # True when this item can be dragged } ``` ### DropTarget.collected_params ```python { "is_over": bool, # True when a draggable is hovering "can_drop": bool, # True when the hovering item can be dropped "item": dict | None, # Data from the hovering draggable item } ``` ## Advanced Usage Examples ### Data Passing with Item Parameter The `item` parameter allows you to pass data from draggable components to drop handlers: ```python demo exec toggle import reflex as rx import reflex_enterprise as rxe class SimpleState(rx.State): message: str = "No items dropped yet" def set_message_from_item(self, item: dict): self.message = f"Dropped: {item['name']}" def simple_draggable(): return rxe.dnd.draggable( rx.box("Drag me!", p=4, bg="blue.100", border="1px solid blue", cursor="grab"), type="simple", item={"name": "test_item", "value": 42}, ) def simple_drop_target(): return rxe.dnd.drop_target( rx.box( rx.text(SimpleState.message), p=4, bg="gray.100", border="2px dashed gray", min_height="100px", ), accept=["simple"], on_drop=SimpleState.set_message_from_item, ) def item_data_example(): return rx.vstack(simple_draggable(), simple_drop_target(), spacing="4") ``` ### Custom Collect Functions The `collect` parameter allows you to access drag and drop state information in real-time: ```python demo exec toggle import reflex as rx import reflex_enterprise as rxe class CollectState(rx.State): drag_info: str = "No drag activity" drop_info: str = "No drop activity" def handle_drop(self, item: dict): self.drop_info = f"Dropped: {item.get('name', 'Unknown')}" return rx.toast(f"Successfully dropped {item.get('name', 'item')}") def collect_draggable(): params = rxe.dnd.Draggable.collected_params return rxe.dnd.draggable( rx.box( rx.vstack( rx.text("Drag me!", weight="bold"), rx.text(f"Dragging: {params.is_dragging}", size="2"), rx.text(f"Can drag: {params.can_drag}", size="2"), spacing="1", ), p=4, bg=rx.cond(params.is_dragging, "blue.200", "blue.100"), border="1px solid blue", cursor=rx.cond(params.is_dragging, "grabbing", "grab"), opacity=rx.cond(params.is_dragging, 0.7, 1.0), ), type="collect_item", item={"id": "collect_test", "name": "Test Item"}, ) def collect_drop_target(): params = rxe.dnd.DropTarget.collected_params return rxe.dnd.drop_target( rx.box( rx.vstack( rx.text("Drop Zone", weight="bold"), rx.text(f"Is over: {params.is_over}", size="2"), rx.text(f"Can drop: {params.can_drop}", size="2"), rx.cond( params.item, rx.text( f"Hovering item: {params.item.get('name', 'Unknown')}", size="2" ), rx.text("No item hovering", size="2"), ), spacing="1", ), p=4, bg=rx.cond( params.is_over & params.can_drop, "green.200", rx.cond(params.is_over, "yellow.200", "gray.100"), ), border=rx.cond( params.is_over & params.can_drop, "2px solid green", rx.cond(params.is_over, "2px solid yellow", "2px dashed gray"), ), min_height="120px", ), accept=["collect_item"], on_drop=CollectState.handle_drop, ) def custom_collect_example(): return rx.vstack( rx.text("Real-time Monitor State", weight="bold", size="4"), rx.hstack( collect_draggable(), collect_drop_target(), spacing="6", align="start" ), rx.text(CollectState.drop_info, size="2", color="gray.600"), spacing="4", ) ``` ## Provider Drag and drop functionality requires the `rxe.dnd.provider` component to wrap your app. The provider is automatically added when using `draggable` or `drop_target` components. For manual control: ```python def app(): return rxe.dnd.provider( # Your app content your_app_content(), backend="HTML5", # or "Touch" for mobile ) ``` ```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 2. **Use descriptive type names** for better debugging 3. **Handle edge cases** in drop handlers (invalid items, etc.) 4. **Provide visual feedback** using collected parameters 5. **Test on mobile devices** with touch backend 6. **Keep item data lightweight** for better performance --- [← Back to main documentation](/docs/enterprise/overview/) # Event Handler API Plugin Source: http://localhost:3000/docs/enterprise/event-handler-api.md --- title: Event Handler API --- _New in reflex-enterprise v0.7.1._ `rxe.EventHandlerAPIPlugin` exposes every registered event handler on your Reflex state as an HTTP `POST` endpoint and auto-generates an OpenAPI 3 specification for them. This turns any Reflex app into a machine-driveable API without writing a single route by hand — great for LLM agents, CLI scripts, end-to-end tests, or external integrations that need to drive the same logic the frontend uses. ```md alert info # Requires `reflex >= 0.9.0` and `reflex-enterprise`. The plugin only works with `rxe.App`. ``` ## Endpoints When the plugin is enabled, the following routes are added to the backend: | Path | Purpose | | --- | --- | | `POST /_reflex/event//` | One endpoint per `@rx.event` handler on every state class. Streams state deltas as newline-delimited JSON. | | `POST /_reflex/retrieve_state` | Returns the full root state `.dict()` for the session token without re-hydrating client storage. | | `GET /_reflex/events/openapi.yaml` | Auto-generated OpenAPI 3 specification describing every endpoint above. | | `GET,HEAD /.well-known/api-catalog` | RFC 9727 API catalog pointing at the OpenAPI spec (RFC 9264 Linkset). | Handler argument names and type annotations are introspected to build each `requestBody` schema, and the docstring's first line becomes the endpoint `summary`. Handlers registered as page `on_load` triggers are listed in the `description` field of the spec so API consumers can tell which endpoint is invoked when a given page is "visited". ## Configuration Add the plugin to the `plugins` list of `rxe.Config` in `rxconfig.py`: ```python import reflex as rx import reflex_enterprise as rxe config = rxe.Config( app_name="my_app", plugins=[ rxe.EventHandlerAPIPlugin( # All three arguments are optional. api_version="1.0.0", contact={"name": "Ops", "email": "ops@example.com"}, license_info={ "name": "Apache 2.0", "url": "https://opensource.org/licenses/Apache-2.0", }, ) ], ) ``` Your app must use `rxe.App()` (not `rx.App()`): ```python import reflex_enterprise as rxe app = rxe.App() ``` ```md alert warning # The backend serves the API on the Reflex backend port (default `http://localhost:8000` in dev, or the `deploy_url` in production). If you're running production with `--single-port`, the API is instead reachable on the frontend port (default `http://localhost:3000`). ``` ## Authentication Every endpoint requires a Bearer token in the `Authorization` header. The token is a random UUID that identifies a **client session**: ``` Authorization: Bearer ``` All calls using the same token share state — the token plays the same role as the per-tab session cookie the browser uses. Generate one with any UUID library: ```bash TOKEN=$(python -c 'import uuid; print(uuid.uuid4())') ``` Reuse `$TOKEN` across calls if you want subsequent requests to see the effects of earlier ones (e.g. create a ticket, then list tickets). Pick a new UUID to get a fresh, independent session. ## Discovering the API The plugin publishes the OpenAPI spec at a well-known location per RFC 9727. Any compliant client can discover it from the catalog: ```bash curl http://localhost:8000/.well-known/api-catalog ``` Response (RFC 9264 Linkset): ```json { "linkset": [ { "anchor": "http://localhost:8000/", "service-desc": [ { "href": "http://localhost:8000/_reflex/events/openapi.yaml", "type": "application/vnd.oai.openapi" } ] } ] } ``` Fetch the spec directly: ```bash curl http://localhost:8000/_reflex/events/openapi.yaml ``` Browse it with any OpenAPI viewer (Swagger UI, Redoc, Scalar, the JetBrains HTTP client, etc.) pointed at that URL. ## Response shape Event handler endpoints return the state deltas produced by the handler as **newline-delimited JSON** (`application/x-ndjson`). Each line is one delta; the stream ends when the handler finishes: ``` {"state.TicketState": {"tickets": [...], "total_count": 3}} {"state.TicketState": {"open_count": 2}} ``` For one-shot clients that just want the final state, simply consume the stream to completion and then (optionally) fetch the full state: ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ http://localhost:8000/_reflex/retrieve_state ``` ```md alert info # Unlike the built-in `hydrate` event, `/_reflex/retrieve_state` does **not** reset client-storage vars (`rx.Cookie`, `rx.LocalStorage`, `rx.SessionStorage`). Use it whenever you want to read state without modifying it. ``` ## The tickets demo app The `reflex-enterprise` repository includes a ready-to-run IT-ticketing demo under `demos/tickets/` that exercises every feature of the plugin. Its `rxconfig.py` is the minimal reference setup: ```python import reflex as rx import reflex_enterprise as rxe config = rxe.Config( app_name="tickets", async_db_url="sqlite+aiosqlite:///tickets.db", db_url="sqlite:///tickets.db", plugins=[ rxe.EventHandlerAPIPlugin( contact={"name": "Reflex Maintainers", "email": "info@reflex.dev"}, license_info={ "name": "Apache 2.0", "url": "https://opensource.org/licenses/Apache-2.0", }, ) ], disable_plugins=[rx.plugins.SitemapPlugin], ) ``` The state class exposes typical CRUD handlers — `create_ticket`, `update_ticket`, `set_status`, `delete_ticket`, `seed`, `clear_all`, plus list/filter/sort/pagination helpers and a `load_tickets` on-load handler. Here's a trimmed excerpt: ```python class TicketState(rx.State): tickets: list[TicketRecord] = [] total_count: int = 0 open_count: int = 0 @rx.event async def create_ticket( self, title: str, description: str = "", priority: str = "medium", assignee: str = "", ) -> None: """Create a new IT support ticket. Args: title: Short summary of the issue. description: Optional long-form description. priority: One of "low", "medium", "high". assignee: Username of the person handling the ticket. """ await self._create_ticket_record( title=title, description=description, priority=priority, assignee=assignee, ) await self._reload_from_db() @rx.event async def set_status(self, ticket_id: str, status: str) -> None: """Set the status of a ticket. Args: ticket_id: The id of the ticket. status: One of "open", "in_progress", "closed". """ ... ``` Because the state's full name is `tickets___tickets____ticket_state`, the generated handler routes live at: ``` POST /_reflex/event/tickets___tickets____ticket_state/ ``` The state full name is built from the Python module path (dot separators become `___`) followed by the class name — inspect the generated `openapi.yaml` if you are unsure of the exact path for a given handler. ### curl examples Assume a dev server running on `http://localhost:8000` and a token in `$TOKEN`: ```bash TOKEN=$(python -c 'import uuid; print(uuid.uuid4())') BASE=http://localhost:8000 TICKET_STATE=$BASE/_reflex/event/tickets___tickets____ticket_state ``` **Discover the API.** ```bash curl $BASE/.well-known/api-catalog curl $BASE/_reflex/events/openapi.yaml ``` **Retrieve the full state dict.** ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ $BASE/_reflex/retrieve_state ``` **Seed some sample tickets.** ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ $TICKET_STATE/seed ``` **Load the first page of tickets into the session.** This mirrors the `on_load` handler the frontend runs when a browser hits `/`: ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ $TICKET_STATE/load_tickets ``` **Create a ticket.** `title` is required; `description`, `priority`, and `assignee` are optional (the server applies the same defaults as in the Python signature): ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"title":"VPN is down","priority":"high","assignee":"alice"}' \ $TICKET_STATE/create_ticket ``` **Change a ticket's status.** ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"ticket_id":"","status":"in_progress"}' \ $TICKET_STATE/set_status ``` **Partial update.** `update_ticket` treats empty strings as "leave unchanged": ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"ticket_id":"","assignee":"bob","priority":"low"}' \ $TICKET_STATE/update_ticket ``` **Delete a ticket.** ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"ticket_id":""}' \ $TICKET_STATE/delete_ticket ``` **Clear the board.** ```bash curl -X POST -H "Authorization: Bearer $TOKEN" \ $TICKET_STATE/clear_all ``` ### Example OpenAPI excerpt The generated spec groups handlers under an OpenAPI `tag` matching the state class name. Here's the entry for `create_ticket`: ```yaml /_reflex/event/tickets___tickets____ticket_state/create_ticket: post: summary: Create a new IT support ticket. description: | title: Short summary of the issue. description: Optional long-form description. priority: One of "low", "medium", "high". assignee: Username of the person handling the ticket. operationId: TicketState_create_ticket tags: [TicketState] requestBody: required: true content: application/json: schema: type: object required: [title] properties: title: {type: string} description: {type: string, default: ""} priority: {type: string, default: medium} assignee: {type: string, default: ""} responses: "200": {$ref: "#/components/responses/StreamedDelta"} "401": {$ref: "#/components/responses/Unauthorized"} ``` ## Driving the app from an LLM Because the OpenAPI spec is self-describing (summaries, parameter types, defaults, on-load references), most LLM agents with HTTP tool access can drive a Reflex app end-to-end without any extra glue code. Give them the spec URL and a natural-language task: > Use the API exposed at `http://localhost:8000/_reflex/events/openapi.yaml` to drive the application. > > Create a new ticket assigned to Masen for investigating RegistrationContext issues in reflex CI. A well-equipped agent will: 1. `GET /_reflex/events/openapi.yaml` and parse the operations. 2. Generate a session token (`uuid4`) to use as the Bearer credential. 3. Call `POST /_reflex/event/.../create_ticket` with a body like `{"title": "Investigate RegistrationContext issues in Reflex CI", "assignee": "Masen", "priority": "medium"}`. 4. Optionally call `/_reflex/retrieve_state` to confirm the ticket landed. Other prompts that work well with the tickets demo: > Using the Reflex API at `http://localhost:8000`, seed the database, then close every ticket currently assigned to `bob`. > Via `http://localhost:8000/_reflex/events/openapi.yaml`, page through every ticket and summarize which assignees have the largest open backlog. > Using the Reflex API at `http://localhost:8000`, create three high-priority tickets for the following issues, then show me the resulting state: ```md alert info # For agents that can't follow `api-catalog` automatically, point them directly at `/_reflex/events/openapi.yaml`. A single URL is enough context for most tool-using models to take it from there. ``` ## Dynamic route variables If any of your pages use dynamic route segments (e.g. `/tickets/[ticket_id]`), the plugin surfaces those as **optional query parameters** on every endpoint so the state can read them via `self.router`: ``` POST /_reflex/event/.../load_ticket_detail?ticket_id= ``` They appear under `components.parameters.route_` in the OpenAPI spec and are referenced from every operation's `parameters` list. ## Security considerations - **The Bearer token is just a session id, not an auth credential.** Anyone who can reach the backend and generate UUIDs can drive the app. Put the API behind your normal auth layer (reverse-proxy, OIDC, VPN, etc.) before exposing it outside trusted networks. - **Every event handler on every state is exposed by default.** If you have privileged handlers, either split them onto a state class you don't want to expose, or run the plugin only in environments where API access is appropriate. - **Handlers that return `rx.redirect(...)` work over the API**, but the redirect is emitted as a state delta rather than an HTTP 3xx — the client sees the URL change, not a browser redirect. This is usually what you want for programmatic clients. # Autocomplete component Source: http://localhost:3000/docs/enterprise/mantine/autocomplete.md --- title: Autocomplete --- `rxe.mantine.autocomplete` is a component for providing suggestions as the user types. It is useful for enhancing user experience by offering relevant options based on input. ```python demo exec import reflex as rx import reflex_enterprise as rxe def autocomplete_example(): return rx.vstack( rxe.mantine.autocomplete( data=["Apple", "Banana", "Cherry", "Date", "Elderberry"], placeholder="Type a fruit", label="Fruit Autocomplete", description="Select a fruit from the list", ), ) ``` # Collapse component Source: http://localhost:3000/docs/enterprise/mantine/collapse.md --- title: Collapse --- `rxe.mantine.collapse` is a component that allows you to create collapsible sections in your application. It is useful for hiding or showing content based on user interaction, such as clicking a button or a link. ```python demo exec import reflex as rx import reflex_enterprise as rxe class CollapseState(rx.State): is_open: bool = False @rx.event def toggle_collapse(self): self.is_open = not self.is_open def collapse_example(): return rx.vstack( rxe.mantine.collapse( rx.text( "This is a collapsible section. Click the button to toggle the collapse.", font_size="lg", ), in_=CollapseState.is_open, label="Collapsible Section", description="Click the button to toggle the collapse.", ), rx.button( "Toggle Collapse", on_click=lambda: CollapseState.toggle_collapse, ), ) ``` # Combobox Source: http://localhost:3000/docs/enterprise/mantine/combobox.md --- title: Combobox --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` `rxe.mantine.combobox` is a wrapping of the mantine component [Combobox](https://mantine.dev/core/combobox/). It is a simple component that can be used to display a list of options, and allows the user to select one or more options from the list. It can be used in various contexts, such as in a form or as a standalone component. ```python import reflex as rx import reflex_enterprise as rxe def combobox_page(): """Combobox demo.""" return rxe.mantine.combobox( rxe.mantine.combobox.target( rx.input(type="button"), ), rxe.mantine.combobox.dropdown( rxe.mantine.combobox.options( rxe.mantine.combobox.option("Option 1"), rxe.mantine.combobox.option("Option 2"), rxe.mantine.combobox.option("Option 3"), ), ), label="Combobox", placeholder="Select a value", ) ``` # Mantine Source: http://localhost:3000/docs/enterprise/mantine.md --- title: Mantine order: 4 --- Mantine is a React component library that provides a set of high-quality components and hooks for building modern web applications. It is designed to be flexible, customizable, and easy to use, making it a popular choice among developers. Some of those components have been integrated into Reflex Enterprise, allowing you to use them in your Reflex applications. The following components are available: - JsonInput - Autocomplete - ComboBox - Multiselect - Pill - PillsInput - TagsInput - Tree - RingProgress - SemiCircleProgress - LoadingOverlay - NumberFormatter - Spoiler - Timeline - Collapse # JSON Input Source: http://localhost:3000/docs/enterprise/mantine/json-input.md --- title: JSON Input --- `rxe.mantine.json_input` is a component that allows you to input JSON data in a user-friendly way. It provides validation and formatting features to ensure that the JSON data is correctly structured. ## Example ```python demo exec import reflex as rx import reflex_enterprise as rxe class JsonInputState(rx.State): json_data: str = "" def set_json_data(self, value: str): self.json_data = value def json_input_example(): return rxe.mantine.json_input( id="json-input", value=JsonInputState.json_data, placeholder="Enter JSON data", label="JSON Input", description="Please enter valid JSON data.", required=True, size="md", format_on_blur=True, on_change=JsonInputState.set_json_data, width="300px", ) ``` # Loading Overlay component Source: http://localhost:3000/docs/enterprise/mantine/loading-overlay.md --- title: Loading Overlay --- `rxe.mantine.loading_overlay` is a component that displays a loading overlay on top of its children. It is useful for indicating that a process is ongoing and prevents user interaction with the underlying content. ```python demo exec import reflex as rx import reflex_enterprise as rxe class LoadingOverlayState(rx.State): loading: bool = False @rx.event def toggle_loading(self): self.loading = not self.loading def loading_overlay_example(): return ( rx.container( rxe.mantine.loading_overlay( rx.text( "Loading Overlay Example", height="200px", width="100px", ), overlay_props={"radius": "sm", "blur": 2}, visible=LoadingOverlayState.loading, z_index=1000, ), ), rx.button("Toggle Loading", on_click=LoadingOverlayState.toggle_loading), ) ``` # MultiSelect component Source: http://localhost:3000/docs/enterprise/mantine/multi-select.md --- title: MultiSelect --- `rxe.mantine.multi_select` is a component for selecting multiple options from a list. It allows users to choose one or more items, making it suitable for scenarios where multiple selections are required. ```python demo exec import reflex as rx import reflex_enterprise as rxe class MultiSelectState(rx.State): selected_fruits: list = [] def set_selected_fruits(self, value: list): self.selected_fruits = value def multi_select_example(): return rx.vstack( rxe.mantine.multi_select( label="Select fruits", placeholder="Pick all that you like", data=["Apple", "Banana", "Cherry", "Date", "Elderberry"], value=MultiSelectState.selected_fruits, on_change=MultiSelectState.set_selected_fruits, ) ) ``` # Number Formatter component Source: http://localhost:3000/docs/enterprise/mantine/number-formatter.md --- title: Number Formatter --- `rxe.mantine.number_formatter` is a component for formatting numbers in a user-friendly way. It allows you to specify the format, precision, and other options for displaying numbers. ```python demo exec import reflex as rx import reflex_enterprise as rxe def number_formatter_example(): return rx.vstack( rxe.mantine.number_formatter( value=100, prefix="$", ), rxe.mantine.number_formatter( value=100, suffix="€", ), rxe.mantine.number_formatter( value=1234567.89, thousand_separator=True, ), ) ``` # Pill Source: http://localhost:3000/docs/enterprise/mantine/pill.md --- title: Pill --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` `rxe.mantine.pill` is a wrapping of the mantine component [Pill](https://mantine.dev/core/pill/). It is a simple component that can be used to display a small piece of information, such as a tag or a label. It can be used in various contexts, such as in a list of tags or labels, or as a standalone component. ```python demo exec import reflex as rx import reflex_enterprise as rxe def pill_page(): """Pill demo.""" return rxe.mantine.pill( "Pill", color="blue", size="md", variant="outline", radius="xl", with_remove_button=True, on_remove=lambda: rx.toast("Pill on_remove triggered"), ) ``` ## Pill Group `rxe.mantine.pill.group` allows grouping multiple `rxe.mantine.pill` components together, with a predefined layout. ```python demo exec import reflex as rx import reflex_enterprise as rxe def pill_group_page(): """Pill demo.""" return rxe.mantine.pill.group( rxe.mantine.pill("Pill 1"), rxe.mantine.pill("Pill 2"), ) ``` ## PillsInput `rxe.mantine.pills_input` is a wrapping of the mantine component [PillsInput](https://mantine.dev/core/pills-input/). It is an utility component that can be used to display a list of tags or labels. It can be used in various contexts, such as in a form or as a standalone component. By itself it does not include any logic, it only renders given children. ```md alert info # For a fully functional out-of-the-box component, consider using [`rxe.mantine.tags_input`](/docs/enterprise/mantine/tags-input/) instead. ``` ## Example ```python demo exec import reflex as rx import reflex_enterprise as rxe class PillInputState(rx.State): """State for the PillsInput demo.""" tags: set[str] = {"Foo", "Bar"} @rx.event def add_tag(self, tag: str): """Add a tag to the list of tags.""" self.tags.add(tag) @rx.event def remove_tag(self, tag: str): """Remove a tag from the list of tags.""" self.tags.remove(tag) def pills_input_page(): """PillsInput demo.""" return rxe.mantine.pills_input( rxe.mantine.pill.group( rx.foreach( PillInputState.tags, lambda tag: rxe.mantine.pill( tag, with_remove_button=True, on_remove=PillInputState.remove_tag(tag), ), ), rxe.mantine.pills_input.field( placeholder="Enter tags", # on_blur=PillInputState.add_tag, ), ), label="PillsInput", id="pills-input", value=["tag1", "tag2"], ) ``` # Ring Progress component Source: http://localhost:3000/docs/enterprise/mantine/ring-progress.md --- title: Ring Progress --- `rxe.mantine.ring_progress` is a component for displaying progress in a circular format. It is useful for visualizing completion percentages or other metrics in a compact and visually appealing way. ```python demo exec import reflex as rx import reflex_enterprise as rxe import random class RingProgressState(rx.State): value: int = 50 @rx.event def random(self): self.value = random.randint(0, 100) def ring_progress_example(): return rx.vstack( rxe.mantine.ring_progress( size=100, sections=[ {"value": RingProgressState.value, "color": "blue"}, ], ), rx.button("Randomize", on_click=RingProgressState.random), ) ``` # Semi Circle Progress component Source: http://localhost:3000/docs/enterprise/mantine/semi-circle-progress.md --- title: Semi Circle Progress --- `rxe.mantine.semi_circle_progress` is a component for displaying progress in a semi-circular format. It is useful for visualizing completion percentages or other metrics in a compact and visually appealing way. ```python demo exec import reflex as rx import reflex_enterprise as rxe import random class SemiCircleProgressState(rx.State): value: int = 50 @rx.event def random(self): self.value = random.randint(0, 100) def semi_circle_progress_example(): return rx.vstack( rxe.mantine.semi_circle_progress( size=100, value=SemiCircleProgressState.value, ), rx.button("Randomize", on_click=SemiCircleProgressState.random), ) ``` # Spoiler component Source: http://localhost:3000/docs/enterprise/mantine/spoiler.md --- title: Spoiler --- `rxe.mantine.spoiler` is a component that allows you to hide or reveal content. It is useful for displaying additional information or details that may not be immediately relevant to the user. ```python demo exec import reflex as rx import reflex_enterprise as rxe def spoiler_example(): return rx.vstack( rxe.mantine.spoiler( "This is a spoiler zone where lorem ipsum text dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...", max_height=50, show_label="Show more", hide_label="Show less", ), ) ``` # TagsInput Source: http://localhost:3000/docs/enterprise/mantine/tags-input.md --- title: TagsInput --- `rxe.mantine.tags_input` is a wrapping of the mantine component [TagsInput](https://mantine.dev/core/tags-input/). It is an utility component that can be used to display a list of tags or labels. It can be used in various contexts, such as in a form or as a standalone component. ```md alert info # You can use the props mentioned in the Mantine documentation, but they need to be passed in snake_case. ``` ## Basic Example ```python demo exec import reflex as rx import reflex_enterprise as rxe def tags_input_simple_page(): """TagsInput demo.""" return rxe.mantine.tags_input( placeholder="Enter tags", label="Press Enter to ad a tag", ) ``` ## State Example ```python demo exec import reflex as rx import reflex_enterprise as rxe class TagsInputState(rx.State): """State for the TagsInput component.""" tags: list[str] = ["Tag1", "Tag2"] @rx.event def update_tags(self, tags: list[str]): """Add a tag to the list of tags.""" self.tags = tags def tags_input_page(): """TagsInput demo.""" return rxe.mantine.tags_input( value=TagsInputState.tags, on_change=TagsInputState.update_tags, placeholder="Enter tags", label="TagsInput", description="This is a TagsInput component", error="", size="md", radius="xl", ) ``` # Timeline component Source: http://localhost:3000/docs/enterprise/mantine/timeline.md --- title: Timeline --- `rxe.mantine.timeline` is a component for displaying a sequence of events or milestones in a linear format. It is useful for visualizing progress, history, or any sequential information. ```python demo exec import reflex as rx import reflex_enterprise as rxe def timeline_example(): return rx.vstack( rxe.mantine.timeline( rxe.mantine.timeline.item( title="Step 1", bullet="•", ), rxe.mantine.timeline.item( title="Step 2", bullet="•", ), rxe.mantine.timeline.item( title="Step 3", bullet="•", ), active=1, bullet_size=24, line_width=2, color="blue", ) ) ``` # Tree component Source: http://localhost:3000/docs/enterprise/mantine/tree.md --- title: Tree --- `rxe.mantine.tree` is a component for displaying hierarchical data in a tree structure. It allows users to expand and collapse nodes, making it easy to navigate through large datasets. ```md alert warning # Due to some technical limitations(pydantic), the tree component only supports 5 levels of depths for the `data` props. ``` ```python demo exec import reflex as rx import reflex_enterprise as rxe def tree_example(): # return rx.text("Placeholder for tree example") return rxe.mantine.tree( data=[ { "value": "0", "label": "Root", "children": [ {"value": "0-1", "label": "Child 1"}, {"value": "0-2", "label": "Child 2"}, ], } ], ) ``` # Interactive Maps Source: http://localhost:3000/docs/enterprise/map.md --- title: Interactive Maps --- ```python exec import reflex as rx import reflex_enterprise as rxe ``` The map components in Reflex Enterprise provide interactive mapping capabilities built on top of **Leaflet**, one of the most popular open-source JavaScript mapping libraries. These components enable you to create rich, interactive maps with markers, layers, controls, and event handling. ```md alert info # All map components are built using Leaflet and react-leaflet, providing a familiar and powerful mapping experience. For advanced Leaflet features, refer to the [Leaflet documentation](https://leafletjs.com/reference.html). ``` 🌍 **[View Live Demo](https://map.reflex.run)** - See the map components in action with interactive examples. ## Installation & Setup Map components are included with `reflex-enterprise`. No additional installation is required. ## Basic Usage Here's a simple example of creating a map with a marker: ```python demo exec import reflex as rx import reflex_enterprise as rxe class MapState(rx.State): center: rxe.map.LatLng = rxe.map.latlng(lat=51.505, lng=-0.09) zoom: float = 13.0 def basic_map(): return rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution='© OpenStreetMap contributors', ), rxe.map.marker( rxe.map.popup("Hello from London!"), position=MapState.center, ), id="basic-map", center=MapState.center, zoom=MapState.zoom, height="400px", width="100%", ) ``` ## Core Components ### Map Container The `rxe.map()` component is the primary container that holds all other map elements: ```python rxe.map( # Child components (markers, layers, controls) id="my-map", center=rxe.map.latlng(lat=51.505, lng=-0.09), zoom=13, height="400px", width="100%", ) ``` **Key Properties:** - `center`: Initial map center coordinates - `zoom`: Initial zoom level (0-18+ depending on tile provider) - `bounds`: Alternative to center/zoom, fits map to bounds - `height`/`width`: Map container dimensions ### Tile Layers Tile layers provide the base map imagery. The most common is OpenStreetMap: ```python rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ) ``` ### Markers Add point markers to specific locations: ```python demo exec import reflex as rx import reflex_enterprise as rxe def markers_example(): return rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), rxe.map.marker( rxe.map.popup( rx.vstack( rx.text("Big Ben", weight="bold"), rx.text("Famous clock tower in London"), spacing="2", ) ), position=rxe.map.latlng(lat=51.4994, lng=-0.1245), ), rxe.map.marker( rxe.map.popup("London Eye"), position=rxe.map.latlng(lat=51.5033, lng=-0.1196), ), id="markers-map", center=rxe.map.latlng(lat=51.501, lng=-0.122), zoom=14, height="400px", width="100%", ) ``` #### Custom Marker Icons To customize a marker's icon, pass a plain dictionary of [Leaflet icon options](https://leafletjs.com/reference.html#icon) to the marker's `icon` prop. There is no `rxe.map.icon` component or `icon` helper in the types module — the prop takes a dict directly: ```python rxe.map.marker( position=rxe.map.latlng(lat=51.505, lng=-0.09), icon={ "iconUrl": "https://example.com/custom-marker.png", "iconSize": [25, 41], "iconAnchor": [12, 41], "popupAnchor": [1, -34], "shadowUrl": "https://example.com/marker-shadow.png", "shadowSize": [41, 41], }, ) ``` ### Vector Layers Draw shapes and areas on the map: ```python demo exec import reflex as rx import reflex_enterprise as rxe def vectors_example(): return rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), # Circle (radius in meters) rxe.map.circle( center=rxe.map.latlng(lat=51.505, lng=-0.09), radius=500, path_options=rxe.map.path_options( color="#ff0000", fill_color="#ff3333", fill_opacity=0.3, weight=2 ), ), # Polygon rxe.map.polygon( positions=[ rxe.map.latlng(lat=51.515, lng=-0.08), rxe.map.latlng(lat=51.515, lng=-0.07), rxe.map.latlng(lat=51.520, lng=-0.07), rxe.map.latlng(lat=51.520, lng=-0.08), ], path_options=rxe.map.path_options( color="#0000ff", fill_color="#3333ff", fill_opacity=0.3 ), ), # Polyline rxe.map.polyline( positions=[ rxe.map.latlng(lat=51.500, lng=-0.095), rxe.map.latlng(lat=51.510, lng=-0.085), rxe.map.latlng(lat=51.515, lng=-0.095), ], path_options=rxe.map.path_options(color="#00ff00", weight=4), ), id="vectors-map", center=rxe.map.latlng(lat=51.510, lng=-0.08), zoom=13, height="400px", width="100%", ) ``` ## Interactive Features ### Event Handling Maps support comprehensive event handling for user interactions: ```python demo exec import reflex as rx import reflex_enterprise as rxe class InteractiveMapState(rx.State): last_click: str = "No clicks yet" current_zoom: float = 13.0 def handle_map_click(self, event): lat = event.get("latlng", {}).get("lat", 0) lng = event.get("latlng", {}).get("lng", 0) self.last_click = f"Clicked at: {lat:.4f}, {lng:.4f}" def handle_zoom_change(self, event): self.current_zoom = float(event.get("target", {}).get("_zoom", 13.0)) def interactive_example(): return rx.vstack( rx.text(f"Last click: {InteractiveMapState.last_click}"), rx.text(f"Current zoom: {InteractiveMapState.current_zoom}"), rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), id="interactive-map", center=rxe.map.latlng(lat=51.505, lng=-0.09), zoom=InteractiveMapState.current_zoom, height="350px", width="100%", on_click=InteractiveMapState.handle_map_click, on_zoom=InteractiveMapState.handle_zoom_change, ), spacing="3", ) ``` Zoom and move events fire rapidly while the user interacts with the map. Debounce them to avoid flooding the backend with state updates: ```python rxe.map( ..., on_zoom=InteractiveMapState.handle_zoom_change.debounce(100), ) ``` When storing the zoom level in state, annotate it as `float` (e.g. `zoom: float = 13.0`) — Leaflet reports fractional zoom values. ### Map Controls Add UI controls for enhanced user interaction: ```python demo exec import reflex as rx import reflex_enterprise as rxe def controls_example(): return rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), rxe.map.zoom_control(position="topright"), rxe.map.scale_control(position="bottomleft"), rxe.map.attribution_control(position="bottomright"), id="controls-map", center=rxe.map.latlng(lat=51.505, lng=-0.09), zoom=13, height="400px", width="100%", ) ``` ## Helper Functions ### Coordinate Creation ```python # Create latitude/longitude coordinates center = rxe.map.latlng(lat=51.505, lng=-0.09, nround=4) # Create bounds bounds = rxe.map.latlng_bounds( corner1_lat=51.49, corner1_lng=-0.11, corner2_lat=51.52, corner2_lng=-0.07 ) ``` ## Map API The Map API provides programmatic control over your maps, allowing you to manipulate the map programmatically from your Reflex state methods. ### Getting the API Reference To access the Map API, you need to get a reference to your map using its ID: ```python map_api = rxe.map.api("my-map-id") ``` `rxe.map.api()` is a function that returns an API object — it is not a class. There is no `rxe.map.API` and no importable `MapApi` type to use in annotations. Call it inline wherever you need it (in a component function or inside an event handler); never store the returned object in a state var, which fails with a `TypeError` about un-annotated parameters. API methods accept **positional arguments only**, with `callback` as the one keyword exception: ```python # Correct - positional arguments map_api.fly_to(coordinates, 15.0) map_api.locate(rxe.map.locate_options(set_view=True)) map_api.fly_to(coordinates, 15.0, callback=my_callback) # Wrong - keyword arguments raise TypeError map_api.fly_to(coordinates, zoom=15.0) ``` ### Interactive Demo Here are some commonly used API methods demonstrated in action: ```python demo exec import reflex as rx import reflex_enterprise as rxe map_api = rxe.map.api("api-demo-map") class MapAPIState(rx.State): current_location: str = "London" def fly_to_london(self): yield map_api.fly_to([51.505, -0.09], 13) self.current_location = "London" def fly_to_paris(self): yield map_api.fly_to([48.8566, 2.3522], 13) self.current_location = "Paris" def map_api_example(): return rx.vstack( rx.text(f"Current location: {MapAPIState.current_location}"), rx.hstack( rx.button("Fly to London", on_click=MapAPIState.fly_to_london), rx.button("Fly to Paris", on_click=MapAPIState.fly_to_paris), rx.button("Zoom Out", on_click=map_api.set_zoom(8)), rx.button( "Log Center", on_click=map_api.get_center(callback=rx.console_log) ), spacing="2", ), rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), id="api-demo-map", center=rxe.map.latlng(lat=51.505, lng=-0.09), zoom=13.0, height="350px", width="100%", ), spacing="3", ) ``` ### Common API Methods **View Control:** - `fly_to(latlng, zoom, options)` - Smooth animated movement to location - `set_view(latlng, zoom, options)` - Instant movement to location - `set_zoom(zoom)` - Change zoom level - `zoom_in()` / `zoom_out()` - Zoom by one level - `fit_bounds(bounds, options)` - Fit map to specific bounds **Location Services:** - `locate(options)` - Get user's current location - `stop_locate()` - Stop location tracking **Information Retrieval:** - `get_center(callback)` - Get current map center - `get_zoom(callback)` - Get current zoom level - `get_bounds(callback)` - Get current map bounds - `get_size(callback)` - Get map container size **Layer Management:** - `add_layer(layer)` - Add a layer to the map - `remove_layer(layer)` - Remove a layer from the map - `has_layer(layer)` - Check if layer exists on map ### Full Leaflet API Access ```md alert info # The Map API provides access to the complete Leaflet map API. Any method available on a Leaflet map instance can be called through the MapAPI instance. Function names are automatically converted from snake_case (Python) to camelCase (JavaScript). ``` This means you can use any method from the [Leaflet Map documentation](https://leafletjs.com/reference.html#map). For example: **Python (snake_case) → JavaScript (camelCase):** - `map_api.pan_to(latlng)` → `map.panTo(latlng)` - `map_api.set_max_bounds(bounds)` → `map.setMaxBounds(bounds)` - `map_api.get_pixel_bounds()` → `map.getPixelBounds()` - `map_api.container_point_to_lat_lng(point)` → `map.containerPointToLatLng(point)` ### Advanced Example ```python demo exec import reflex as rx import reflex_enterprise as rxe class AdvancedMapState(rx.State): constraints_applied: bool = False location_tracking: bool = False location_status: str = "Location tracking disabled" def set_location_status(self, status: str): self.location_status = status def setup_map_constraints(self): map_api = rxe.map.api("advanced-demo-map") # Set maximum bounds (restrict panning to London area) max_bounds = rxe.map.latlng_bounds( corner1_lat=51.4, corner1_lng=-0.3, corner2_lat=51.6, corner2_lng=0.1 ) yield map_api.set_max_bounds(max_bounds) # Set min/max zoom levels yield map_api.set_min_zoom(10) yield map_api.set_max_zoom(16) # Disable scroll wheel zoom yield map_api.scroll_wheel_zoom(False) self.constraints_applied = True def remove_constraints(self): map_api = rxe.map.api("advanced-demo-map") # Remove bounds restriction yield map_api.set_max_bounds(None) # Reset zoom limits yield map_api.set_min_zoom(1) yield map_api.set_max_zoom(18) # Re-enable scroll wheel zoom yield map_api.scroll_wheel_zoom(True) self.constraints_applied = False def toggle_location_tracking(self): map_api = rxe.map.api("advanced-demo-map") if self.location_tracking == False: # Start location tracking locate_options = rxe.map.locate_options( set_view=True, max_zoom=16, timeout=10000, enable_high_accuracy=True, watch=False, # Single location request ) yield map_api.locate(locate_options) self.location_tracking = True self.location_status = "Requesting location..." else: # Stop location tracking yield map_api.stop_locate() self.location_tracking = False self.location_status = "Location tracking disabled" def advanced_example(): return rx.vstack( rx.hstack( rx.button( rx.cond( AdvancedMapState.constraints_applied, "Remove Constraints", "Apply Constraints", ), on_click=rx.cond( AdvancedMapState.constraints_applied, AdvancedMapState.remove_constraints, AdvancedMapState.setup_map_constraints, ), color_scheme="blue", ), rx.button( rx.cond( AdvancedMapState.location_tracking, "Disable Location", "Enable Location", ), on_click=AdvancedMapState.toggle_location_tracking, color_scheme="green", ), spacing="3", ), rx.text(f"Status: {AdvancedMapState.location_status}"), rx.text( rx.cond( AdvancedMapState.constraints_applied, "Constraints: Applied (restricted to London area, zoom 10-16, no scroll wheel)", "Constraints: None", ) ), rxe.map( rxe.map.tile_layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors", ), rxe.map.marker( rxe.map.popup("Try panning and zooming when constraints are applied!"), position=rxe.map.latlng(lat=51.505, lng=-0.09), ), id="advanced-demo-map", center=rxe.map.latlng(lat=51.505, lng=-0.09), zoom=12.0, height="400px", width="100%", on_locationfound=lambda e: AdvancedMapState.set_location_status( "Location found!" ), on_locationerror=lambda e: AdvancedMapState.set_location_status( "Location error - permission denied or unavailable" ), ), spacing="3", ) ``` ### Callback Handling Many API methods that retrieve information require callbacks to handle the results: ```python class CallbackMapState(rx.State): map_info: str = "" def handle_center_result(self, result): lat = result.get("lat", 0) lng = result.get("lng", 0) self.map_info = f"Center: {lat:.4f}, {lng:.4f}" def handle_bounds_result(self, result): # result will contain bounds information self.map_info = f"Bounds: {result}" def get_map_info(self): map_api = rxe.map.api("info-map") yield map_api.get_center(self.handle_center_result) # or yield map_api.get_bounds(self.handle_bounds_result) ``` ## Available Events The map components support a comprehensive set of events: **Map Events:** - `on_click`, `on_dblclick` - Mouse click events - `on_zoom`, `on_zoom_start`, `on_zoom_end` - Zoom events - `on_move`, `on_move_start`, `on_move_end` - Pan events - `on_resize` - Map container resize - `on_load`, `on_unload` - Map lifecycle **Location Events:** - `on_locationfound`, `on_locationerror` - Geolocation **Layer Events:** - `on_layeradd`, `on_layerremove` - Layer management **Popup Events:** - `on_popupopen`, `on_popupclose` - Popup lifecycle - `on_tooltipopen`, `on_tooltipclose` - Tooltip lifecycle ## Common Patterns ### Dynamic Markers ```python class DynamicMapState(rx.State): markers: list[dict] = [ {"lat": 51.505, "lng": -0.09, "title": "London"}, {"lat": 48.8566, "lng": 2.3522, "title": "Paris"}, {"lat": 52.5200, "lng": 13.4050, "title": "Berlin"}, ] def dynamic_markers(): return rxe.map( rxe.map.tile_layer(url="..."), rx.foreach( DynamicMapState.markers, lambda marker: rxe.map.marker( rxe.map.popup(marker["title"]), position=rxe.map.latlng(lat=marker["lat"], lng=marker["lng"]), ), ), # ... map configuration ) ``` ## Best Practices 1. **Always include attribution** for tile providers 2. **Set reasonable zoom levels** (typically 1-18) 3. **Use bounds for multiple markers** instead of arbitrary center/zoom 4. **Handle loading states** for dynamic map content 5. **Optimize marker rendering** for large datasets using clustering 6. **Test on mobile devices** for touch interactions --- [← Back to main documentation](/docs/enterprise/overview/) # Reflex Enterprise Source: http://localhost:3000/docs/enterprise/overview.md --- title: Reflex Enterprise --- ```python exec import reflex as rx try: import reflex_enterprise as rxe from reflex_enterprise.components.ag_grid.resource import RendererParams except ImportError: rxe = None RendererParams = None ``` Reflex Enterprise is a package containing paid features built on top of Reflex. The full [End-User License Agreement (EULA)](https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/enterprise/LICENSE) for Reflex Enterprise is published in the Reflex repository. ```md alert info # Enterprise components are available to free users. Free apps can use these components with a "Built with Reflex" badge shown in the bottom right corner of the app. For more information on the badge, visit [Built with Reflex](/docs/enterprise/built-with-reflex/). ``` ## Installation `reflex-enterprise` must be installed alongside `reflex` to access the enterprise features. Install it from PyPI: ```bash pip install reflex-enterprise ``` ## Features ```python exec # Create master data organized by category categories_data = [ { "category": "Configuration", "description": "Core enterprise features for deployment and branding", "count": 2, "components": [ { "feature": "show_built_with_reflex", "description": "Toggle the 'Built with Reflex' badge in your app", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/built-with-reflex", }, { "feature": "use_single_port", "description": "Enable single-port deployment by proxying backend to frontend", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/single-port-proxy", }, ], }, { "category": "Authentication", "description": "OIDC authentication with a secure-by-default model", "count": 6, "components": [ { "feature": "AuthPlugin", "description": "OIDC (OpenID Connect) authentication with secure-by-default protection", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/overview", }, { "feature": "Secure by Default", "description": "Pages, event handlers, fields, and computed vars require login unless opted out", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/secure-by-default", }, { "feature": "OIDC Providers", "description": "Built-in generic OIDC provider plus named and multi-provider setups", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/providers", }, { "feature": "Custom Auth Pages", "description": "Replace the rendered login, callback, and logout pages", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/custom-pages", }, { "feature": "Testing", "description": "Unit-test authorization checks and exercise the full OIDC flow against a mock IdP", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/testing", }, { "feature": "Deploying to Production", "description": "HTTPS and cookie requirements, the exact redirect URI, reverse proxies, and a troubleshooting reference", "cloud_tier": "Enterprise", "self_hosted_tier": "Enterprise", "link": "/enterprise/auth/deployment", }, ], }, { "category": "Testing", "description": "pytest plugin for end-to-end testing of Reflex apps", "count": 1, "components": [ { "feature": "reflex_app fixture", "description": "Boot the app under test with reflex run and drive it with browser tooling", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/testing", }, ], }, { "category": "AGGrid and AGChart", "description": "Advanced data visualization and grid components", "count": 2, "components": [ { "feature": "AgGrid", "description": "Advanced data grid with enterprise features (sorting, filtering, grouping)", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/ag-grid", }, { "feature": "AGCharts", "description": "Interactive charts and data visualization components", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/ag-chart", }, ], }, { "category": "Interactive Components", "description": "Interactive UI features including drag-and-drop and mapping", "count": 2, "components": [ { "feature": "Drag and Drop", "description": "Drag and drop functionality for interactive UI elements", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/drag-and-drop", }, { "feature": "Mapping", "description": "Interactive maps with markers, layers, and controls", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/map", }, ], }, { "category": "Mantine", "description": "Rich UI components from Mantine library", "count": 15, "components": [ { "feature": "Autocomplete", "description": "Auto-completing text input with dropdown suggestions", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/autocomplete", }, { "feature": "Combobox", "description": "Searchable dropdown with custom options and filtering", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/combobox", }, { "feature": "Multi Select", "description": "Multi-selection dropdown with tags and search", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/multi-select", }, { "feature": "Tags Input", "description": "Input field for creating and managing tags", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/tags-input", }, { "feature": "Json Input", "description": "JSON editor with syntax highlighting and validation", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/json-input", }, { "feature": "Pill", "description": "Small rounded elements for tags, badges, and labels", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/pill", }, { "feature": "Tree", "description": "Hierarchical tree view with expandable nodes", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/tree", }, { "feature": "Timeline", "description": "Timeline component for displaying chronological events", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/timeline", }, { "feature": "Number Formatter", "description": "Format and display numbers with customizable formatting", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/number-formatter", }, { "feature": "Ring Progress", "description": "Circular progress indicator with customizable styling", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/ring-progress", }, { "feature": "Semi Circle Progress", "description": "Semi-circular progress indicator for dashboards", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/semi-circle-progress", }, { "feature": "Loading Overlay", "description": "Loading overlay with spinner for async operations", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/loading-overlay", }, { "feature": "Spoiler", "description": "Collapsible content container with show/hide toggle", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/spoiler", }, { "feature": "Collapse", "description": "Animated collapsible content with smooth transitions", "cloud_tier": "Free", "self_hosted_tier": "Free", "link": "/enterprise/mantine/collapse", }, ], }, ] if rxe is not None: @rxe.arrow_func def custom_link_renderer(params: RendererParams): """Custom cell renderer for links in AG Grid.""" return rx.link( params.value, href=params.data.link, ) grid = rxe.ag_grid( column_defs=[ { "field": "category", "header_name": "Category", "cell_renderer": "agGroupCellRenderer", "suppress_menu": True, "width": 220, }, { "field": "description", "width": 500, }, { "field": "count", "header_name": "Components", "width": 150, }, ], row_data=categories_data, master_detail=True, detail_cell_renderer_params={ "detail_grid_options": { "column_defs": [ { "field": "feature", "header_name": "Component/Feature", "cell_renderer": custom_link_renderer, "width": 250, }, { "field": "description", "header_name": "Description", "width": 350, }, {"field": "cloud_tier", "header_name": "Cloud Tier", "width": 120}, { "field": "self_hosted_tier", "header_name": "Self-hosted Tier", "width": 140, }, ], "suppress_context_menu": True, "row_height": 35, }, "get_detail_row_data": lambda params: rx.vars.function.FunctionStringVar( "params.successCallback" ).call(params.data.components), }, id="features-grid", width="100%", detail_row_height=200, detail_row_auto_height=True, height="400px", loading=False, ) grid.api.set_grid_option("suppressContextMenu", True) else: grid = rx.text( "Reflex Enterprise not available. Install with: pip install reflex-enterprise" ) ``` ```python eval grid ``` ## Usage of reflex_enterprise. Use `rxe.App` as your `app` to enable Enterprise components and `rxe.Config` options. ### In the main file Create the app with `rxe.App()`: ```python import reflex_enterprise as rxe app = rxe.App() ``` ### In rxconfig.py ```python import reflex_enterprise as rxe config = rxe.Config( app_name="MyApp", ... # accepts rx.Config arguments plus rxe.Config options ) ``` # Basic Flow Example Source: http://localhost:3000/docs/enterprise/react-flow/basic-flow.md This example demonstrates a simple flow diagram with three nodes and two edges, showing how nodes can be connected and how edges can be animated. ### Nodes - Input Node – starting point - Default Node – standard content node - Output Node – endpoint of the flow ### Edges - Input → Default (animated) - Default → Output ### Interactivity - Nodes can be moved - Edges update dynamically - Users can drag from handles to create new edges - Zoom, pan, and mini-map controls are available ### Visual Layout - Flow fits viewport automatically - Background grid for orientation - Light and dark color modes supported ### Example Flow ```python demo exec import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import Node, Edge # Common style for all nodes node_style = { "backgroundColor": "#ffcc00", "color": "#000000", "padding": "10px", "borderRadius": "5px", } class FlowState(rx.State): nodes: list[Node] = [ { "id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Input Node"}, "style": node_style, }, { "id": "2", "type": "default", "position": {"x": 300, "y": 200}, "data": {"label": "Default Node"}, "style": node_style, }, { "id": "3", "type": "output", "position": {"x": 500, "y": 100}, "data": {"label": "Output Node"}, "style": node_style, }, ] edges: list[Edge] = [ {"id": "e1-2", "source": "1", "target": "2", "animated": True}, {"id": "e2-3", "source": "2", "target": "3"}, ] def flow_example(): return rx.box( rxe.flow( # Core flow components rxe.flow.controls(), rxe.flow.background(), rxe.flow.mini_map(), # Flow configuration default_nodes=FlowState.nodes, default_edges=FlowState.edges, nodes=FlowState.nodes, edges=FlowState.edges, # Visual settings fit_view=True, attribution_position="bottom-right", ), height="100vh", width="100vw", ) ``` # Flow Components Source: http://localhost:3000/docs/enterprise/react-flow/components.md This page documents the main components provided by the `rxe.flow` library. ## rxe.flow.provider The `FlowProvider` component is a context provider that makes it possible to access a flow’s internal state outside of the `` component. Many of the hooks we provide rely on this component to work. **Props:** - `initial_nodes`: `Sequence[Node]` - These nodes are used to initialize the flow. They are not dynamic. - `default_edges`: `Sequence[Edge]` - These edges are used to initialize the flow. They are not dynamic. - `initial_width`: `float` - The initial width is necessary to be able to use fitView on the server. - `initial_height`: `float` - The initial height is necessary to be able to use fitView on the server. - `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided. - `initial_fit_view_options`: `FitViewOptions` - You can provide an object of options to customize the initial fitView behavior. - `initial_min_zoom`: `float` - Initial minimum zoom level. - `initial_max_zoom`: `float` - Initial maximum zoom level. - `node_origin`: `NodeOrigin` - The origin of the node to use when placing it in the flow or looking up its x and y position. - `node_extent`: `CoordinateExtent` - The boundary a node can be moved in. `rxe.flow.provider` does not accept any event handlers. All event handlers (`on_nodes_change`, `on_edges_change`, `on_connect`, etc.) must be set on the inner `rxe.flow` component. **Example:** ```python rx.box( rxe.flow.provider( rxe.flow( rxe.flow.background(), nodes=FlowState.nodes, edges=FlowState.edges, on_nodes_change=lambda changes: FlowState.set_nodes( rxe.flow.util.apply_node_changes(FlowState.nodes, changes) ), on_edges_change=lambda changes: FlowState.set_edges( rxe.flow.util.apply_edge_changes(FlowState.edges, changes) ), fit_view=True, ) ), height="100vh", width="100vw", ) ``` ## rxe.flow The `Flow` component is the main component that renders the flow. It takes in nodes and edges, and provides event handlers for user interactions. **Props:** - `nodes`: `Sequence[Node]` - An array of nodes to render in a controlled flow. - `edges`: `Sequence[Edge]` - An array of edges to render in a controlled flow. - `default_nodes`: `Sequence[Node]` - The initial nodes to render in an uncontrolled flow. - `default_edges`: `Sequence[Edge]` - The initial edges to render in an uncontrolled flow. - `node_types`: `Mapping[str, Any]` - Custom node types. - `edge_types`: `Mapping[str, Any]` - Custom edge types. - `on_nodes_change`: Event handler for when nodes change. - `on_edges_change`: Event handler for when edges change. - `on_connect`: Event handler for when a connection is made. - `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided. - `fit_view_options`: `FitViewOptions` - Options for `fit_view`. - `style`: The style of the component. ## rxe.flow.background The `Background` component renders a background for the flow. It can be a pattern of lines, dots, or a cross. **Props:** - `color`: `str` - Color of the pattern. - `bg_color`: `str` - Color of the background. - `variant`: `Literal["lines", "dots", "cross"]` - The type of pattern to render. - `gap`: `float | tuple[float, float]` - The gap between patterns. - `size`: `float` - The size of the pattern elements. **Example:** ```python rxe.flow.background(variant="dots", gap=20, size=1) ``` ## rxe.flow.controls The `Controls` component renders a panel with buttons to zoom in, zoom out, fit the view, and lock the viewport. **Props:** - `show_zoom`: `bool` - Whether to show the zoom buttons. - `show_fit_view`: `bool` - Whether to show the fit view button. - `show_interactive`: `bool` - Whether to show the lock button. - `position`: `PanelPosition` - The position of the controls on the pane. **Example:** ```python rxe.flow.controls() ``` ## rxe.flow.mini_map The `MiniMap` component renders a small overview of your flow. **Props:** - `node_color`: `str | Any` - Color of nodes on minimap. - `node_stroke_color`: `str | Any` - Stroke color of nodes on minimap. - `pannable`: `bool` - Determines whether you can pan the viewport by dragging inside the minimap. - `zoomable`: `bool` - Determines whether you can zoom the viewport by scrolling inside the minimap. - `position`: `PanelPosition` - Position of minimap on pane. **Example:** ```python rxe.flow.mini_map(pannable=True, zoomable=True) ``` # Edges Source: http://localhost:3000/docs/enterprise/react-flow/edges.md Edges connect nodes together in a flow. This page explains how to define, customize, and interact with edges in Reflex Flow. ## The Edge Type An edge is represented as a Python dictionary with the following fields: - `id` (`str`) – Unique identifier for the edge. - `source` (`str`) – ID of the source node. - `target` (`str`) – ID of the target node. - `type` (`str`) – Edge type defined in `edge_types`. - `sourceHandle` (`str | None`) – Optional source handle ID. - `targetHandle` (`str | None`) – Optional target handle ID. - `animated` (`bool`) – Whether the edge should animate. - `hidden` (`bool`) – Whether the edge is hidden. - `deletable` (`bool`) – Whether the edge can be removed. - `selectable` (`bool`) – Whether the edge can be selected. - `data` (`dict`) – Arbitrary metadata. - `label` (`Any`) – Label rendered along the edge. - `style` (`dict`) – Custom styles. - `className` (`str`) – CSS class for the edge. ## Basic Edge Types Reflex Flow comes with several built-in edge types: ### Default Edge Types ```python edges: list[Edge] = [ {"id": "e1", "source": "1", "target": "2", "type": "default"}, {"id": "e2", "source": "2", "target": "3", "type": "straight"}, {"id": "e3", "source": "3", "target": "4", "type": "step"}, {"id": "e4", "source": "4", "target": "5", "type": "smoothstep"}, {"id": "e5", "source": "5", "target": "6", "type": "bezier"}, ] ``` - **default** – Standard curved edge - **straight** – Direct line between nodes - **step** – Right-angled path with steps - **smoothstep** – Smooth right-angled path - **bezier** – Curved bezier path ## Edge Styling ### Basic Styling Add visual styling to edges using the `style` property: ```python edges: list[Edge] = [ { "id": "styled-edge", "source": "1", "target": "2", "style": { "stroke": "#ff6b6b", "strokeWidth": 3, }, } ] ``` ### Animated Edges Make edges animate with flowing dots: ```python edges: list[Edge] = [ { "id": "animated-edge", "source": "1", "target": "2", "animated": True, "style": {"stroke": "#4dabf7"}, } ] ``` ### Edge Labels Add text labels to edges: ```python edges: list[Edge] = [ { "id": "labeled-edge", "source": "1", "target": "2", "label": "Connection", "style": {"stroke": "#51cf66"}, } ] ``` ## Custom Edges React Flow in Reflex also allows you to define custom edge types. This is useful when you want edges to carry extra functionality (like buttons, labels, or dynamic styling) beyond the default straight or bezier connectors. ```python demo exec import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import ( ConnectionInProgress, Edge, NoConnection, Node, Position, ) class SimpleEdgeDemoState(rx.State): nodes: list[Node] = [ { "id": "1", "position": {"x": 0, "y": 0}, "data": {"label": "Node A"}, "style": { "color": "#000000", }, }, { "id": "2", "position": {"x": 250, "y": 150}, "data": {"label": "Node B"}, "style": { "color": "#000000", }, }, ] edges: list[Edge] = [{"id": "e1-2", "source": "1", "target": "2", "type": "button"}] @rx.event def set_nodes(self, nodes: list[Node]): self.nodes = nodes @rx.event def set_edges(self, edges: list[Edge]): self.edges = edges @rx.event def handle_connect_end( self, connection_status: NoConnection | ConnectionInProgress, ): if not connection_status["isValid"]: new_edge = { "id": f"{connection_status['fromNode']['id']}-{connection_status['toNode']['id']}", "source": connection_status["fromNode"]["id"], "target": connection_status["toNode"]["id"], "type": "button", } self.edges.append(new_edge) @rx.memo def button_edge( id: rx.Var[str], sourceX: rx.Var[float], sourceY: rx.Var[float], targetX: rx.Var[float], targetY: rx.Var[float], sourcePosition: rx.Var[Position], targetPosition: rx.Var[Position], markerEnd: rx.Var[str], ) -> rx.Fragment: bezier_path = rxe.components.flow.util.get_bezier_path( source_x=sourceX, source_y=sourceY, target_x=targetX, target_y=targetY, source_position=sourcePosition, target_position=targetPosition, ) mid_x = bezier_path.label_x mid_y = bezier_path.label_y return rx.fragment( rxe.flow.base_edge(path=bezier_path.path, markerEnd=markerEnd), rxe.flow.edge_label_renderer( rx.el.div( rx.el.button( "×", class_name=( "w-[30px] h-[30px] border-2 border-gray-200 bg-gray-200 text-black rounded-full text-[12px] pt-0 cursor-pointer hover:bg-gray-400 hover:text-white" ), on_click=rx.run_script( rxe.flow.api.set_edges( rx.vars.FunctionStringVar.create( "Array.prototype.filter.call" ).call( rxe.flow.api.get_edges(), rx.Var(f"((edge) => edge.id !== {id})"), ), ) ), style={ "position": "absolute", "left": f"{mid_x}px", "top": f"{mid_y}px", "transform": "translate(-50%, -50%)", "pointerEvents": "all", }, ), ) ), ) def very_simple_custom_edge_example(): return rx.box( rxe.flow( rxe.flow.background(), default_nodes=SimpleEdgeDemoState.nodes, default_edges=SimpleEdgeDemoState.edges, nodes=SimpleEdgeDemoState.nodes, edges=SimpleEdgeDemoState.edges, on_connect=lambda connection: SimpleEdgeDemoState.set_edges( rxe.flow.util.add_edge(connection, SimpleEdgeDemoState.edges) ), on_connect_end=lambda status, event: SimpleEdgeDemoState.handle_connect_end( status ), edge_types={"button": button_edge}, fit_view=True, ), height="100vh", width="100%", ) ``` # Example React Flow Components Source: http://localhost:3000/docs/enterprise/react-flow/examples.md This section showcases examples of interactive flow components built with Reflex and Reflex Enterprise. Learn how to create dynamic nodes, edges, and custom behaviors for building flow diagrams in your React apps. ## Add Node on Edge Drop In this example, we demonstrate how to dynamically add nodes to a flow when a connection is dropped onto the canvas. When the user drops a connection, a new node is created at the drop point, and an edge is added between the source node and the new node. ```python demo exec import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import ( ConnectionInProgress, Edge, NoConnection, Node, XYPosition, ) node_style = { "color": "#000000", } initial_nodes: list[Node] = [ { "id": "0", "type": "input", "data": {"label": "Node"}, "position": {"x": 0, "y": 50}, "style": node_style, }, ] class AddNodesOnEdgeDropState(rx.State): nodes: rx.Field[list[Node]] = rx.field(default_factory=lambda: initial_nodes) edges: rx.Field[list[Edge]] = rx.field(default_factory=list) node_id: int = 1 @rx.event def increment(self): self.node_id += 1 @rx.event def set_nodes(self, nodes: list[Node]): self.nodes = nodes @rx.event def set_edges(self, edges: list[Edge]): self.edges = edges @rx.event def handle_connect_end( self, connection_status: NoConnection | ConnectionInProgress, event: rx.event.PointerEventInfo, flow_position: XYPosition, ): if not connection_status["isValid"]: node_id = str(self.node_id) self.increment() self.nodes.append({ "id": node_id, "position": flow_position, "data": {"label": f"Node {node_id}"}, "origin": (0.5, 0.0), "style": node_style, }) self.edges.append({ "id": node_id, "source": connection_status["fromNode"]["id"], "target": node_id, "style": node_style, }) def add_node_on_edge_drop(): return rx.box( rxe.flow.provider( rxe.flow( rxe.flow.controls(), rxe.flow.mini_map(), rxe.flow.background(), on_connect=lambda connection: AddNodesOnEdgeDropState.set_edges( rxe.flow.util.add_edge(connection, AddNodesOnEdgeDropState.edges) ), on_connect_end=( lambda connection_status, event: ( AddNodesOnEdgeDropState.handle_connect_end( connection_status, event, rxe.flow.api.screen_to_flow_position( x=event.client_x, y=event.client_y, ), ) ) ), nodes=AddNodesOnEdgeDropState.nodes, edges=AddNodesOnEdgeDropState.edges, default_nodes=AddNodesOnEdgeDropState.nodes, default_edges=AddNodesOnEdgeDropState.edges, on_nodes_change=lambda changes: AddNodesOnEdgeDropState.set_nodes( rxe.flow.util.apply_node_changes( AddNodesOnEdgeDropState.nodes, changes ) ), on_edges_change=lambda changes: AddNodesOnEdgeDropState.set_edges( rxe.flow.util.apply_edge_changes( AddNodesOnEdgeDropState.edges, changes ) ), fit_view=True, fit_view_options={"padding": 2}, node_origin=(0.5, 0.0), ) ), height="100vh", width="100vw", ) ``` ## Connection Limit on Custom Node This example demonstrates how to create a custom node with a connection limit on its handle. The handle can be configured to allow a specific number of connections, or no connections at all, using the isConnectable property. This is useful when you want to restrict the number of connections a node can have. ```python demo exec import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import Edge, HandleType, Node, Position node_style = { "color": "#000000", } class ConnectionLimitState(rx.State): nodes: rx.Field[list[Node]] = rx.field( default_factory=lambda: [ { "id": "1", "type": "input", "data": {"label": "Node 1"}, "position": {"x": 0, "y": 25}, "sourcePosition": "right", "style": node_style, }, { "id": "2", "type": "custom", "data": {}, "position": {"x": 250, "y": 50}, "style": node_style, }, { "id": "3", "type": "input", "data": {"label": "Node 2"}, "position": {"x": 0, "y": 100}, "sourcePosition": "right", "style": node_style, }, ] ) edges: rx.Field[list[Edge]] = rx.field(default_factory=list) @rx.event def set_nodes(self, nodes: list[Node]): self.nodes = nodes @rx.event def set_edges(self, edges: list[Edge]): self.edges = edges @rx.memo def custom_handle( type: rx.Var[HandleType], position: rx.Var[Position], connection_count: rx.Var[int] ) -> rxe.components.flow.Handle: connections = rxe.flow.api.get_node_connections() return rxe.flow.handle( type=type, position=position, connection_count=connection_count, is_connectable=connections.length() < connection_count.guess_type(), ) @rx.memo def custom_node() -> rx.el.Div: return rx.el.div( custom_handle(type="target", position="left", connection_count=1), rx.el.div("← Only one edge allowed"), class_name="border border-1 p-2 rounded-sm", border_color=rx.color_mode_cond("black", ""), color="black", bg="white", ) def connection_limit(): return rx.box( rxe.flow( rxe.flow.background(), nodes=ConnectionLimitState.nodes, edges=ConnectionLimitState.edges, default_nodes=ConnectionLimitState.nodes, default_edges=ConnectionLimitState.edges, on_nodes_change=lambda changes: ConnectionLimitState.set_nodes( rxe.flow.util.apply_node_changes(ConnectionLimitState.nodes, changes) ), on_edges_change=lambda changes: ConnectionLimitState.set_edges( rxe.flow.util.apply_edge_changes(ConnectionLimitState.edges, changes) ), on_connect=lambda connection: ConnectionLimitState.set_edges( rxe.flow.util.add_edge(connection, ConnectionLimitState.edges) ), node_types={ "custom": rx.vars.function.ArgsFunctionOperation.create( (), custom_node() ) }, color_mode="light", fit_view=True, ), height="100vh", width="100vw", ) ``` # Hooks (API) Source: http://localhost:3000/docs/enterprise/react-flow/hooks.md The `rxe.flow.api` module provides hooks to interact with the Flow instance. These hooks are wrappers around the `useReactFlow` hook from React Flow. These hooks rely on the flow being wrapped in `rxe.flow.provider`. ## Node Hooks - `get_nodes()`: Returns an array of all nodes in the flow. - `set_nodes(nodes)`: Sets the nodes in the flow. - `add_nodes(nodes)`: Adds nodes to the flow. - `get_node(id)`: Returns a node by its ID. - `update_node(id, node_update, replace=False)`: Updates a node in the flow. - `update_node_data(id, data_update, replace=False)`: Updates a node's data in the flow. ## Edge Hooks - `get_edges()`: Returns an array of all edges in the flow. - `set_edges(edges)`: Sets the edges in the flow. - `add_edges(edges)`: Adds edges to the flow. - `get_edge(id)`: Returns an edge by its ID. - `update_edge(id, edge_update, replace=False)`: Updates an edge in the flow. - `update_edge_data(id, data_update, replace=False)`: Updates an edge's data in the flow. ## Viewport Hooks - `screen_to_flow_position(x, y, snap_to_grid=False)`: Translates a screen pixel position to a flow position. - `flow_to_screen_position(x, y)`: Translates a position inside the flow’s canvas to a screen pixel position. Use `screen_to_flow_position` to convert pointer coordinates from an event into canvas coordinates — for example, to place a node where the user dropped an unfinished connection: ```python rxe.flow( ..., on_connect_end=lambda connection_status, event: FlowState.handle_connect_end( connection_status, rxe.flow.api.screen_to_flow_position( x=event.client_x, y=event.client_y, ), ), ) ``` The conversion happens on the client, and the resulting `XYPosition` is passed to the state event handler as an argument. See the "Add Node on Edge Drop" example on the Examples page for a complete implementation. ## Other Hooks - `to_object()`: Converts the React Flow state to a JSON object. - `get_intersecting_nodes(node, partially=True, nodes=None)`: Find all the nodes currently intersecting with a given node or rectangle. - `get_node_connections(id=None, handle_type=None, handle_id=None)`: This hook returns an array of connections on a specific node, handle type ("source", "target") or handle ID. - `get_connection()`: Returns the current connection state when there is an active connection interaction. # Adding Interactivity to Your Flow Source: http://localhost:3000/docs/enterprise/react-flow/interactivity.md This guide shows how to create an interactive flow in Reflex, allowing you to select, drag, and connect nodes and edges. ## Define the State We start by defining the nodes and edges of the flow. The `FlowState` class holds the nodes and edges as state variables and includes event handlers to respond to changes. ```python import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import Node, Edge class FlowState(rx.State): nodes: list[Node] = [ { "id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Node 1"}, }, { "id": "2", "type": "default", "position": {"x": 300, "y": 200}, "data": {"label": "Node 2"}, }, ] edges: list[Edge] = [ { "id": "e1-2", "source": "1", "target": "2", "label": "Connection", "type": "step", } ] ``` ## Add Event Handlers Event handlers allow the flow to respond to user interactions such as dragging nodes, updating edges, or creating new connections. ```python @rx.event def set_nodes(self, nodes: list[Node]): self.nodes = nodes @rx.event def set_edges(self, edges: list[Edge]): self.edges = edges ``` - set_nodes updates nodes when they are moved or edited. - set_edges updates edges when they are modified or deleted. Note that these are plain setters — they receive the already-updated list. The change-application logic lives in the component wiring, shown next. ## Render the Interactive Flow Finally, we render the flow using **rxe.flow**, passing in the state and event handlers. Additional UI features include zoom/pan controls, a background grid, and a mini-map for navigation. ```python def interactive_flow(): return rx.box( rxe.flow( rxe.flow.controls(), rxe.flow.background(), rxe.flow.mini_map(), nodes=FlowState.nodes, edges=FlowState.edges, on_nodes_change=lambda node_changes: FlowState.set_nodes( rxe.flow.util.apply_node_changes(FlowState.nodes, node_changes) ), on_edges_change=lambda edge_changes: FlowState.set_edges( rxe.flow.util.apply_edge_changes(FlowState.edges, edge_changes) ), on_connect=lambda connection: FlowState.set_edges( rxe.flow.util.add_edge(connection, FlowState.edges) ), fit_view=True, attribution_position="bottom-right", ), height="100vh", width="100vw", ) ``` - `on_nodes_change` fires when nodes are dragged, selected, resized, or removed. - `on_edges_change` fires when edges are selected or removed. - `on_connect` fires when the user drags a connection between two handles; `rxe.flow.util.add_edge` builds the new edge list from the connection. Make sure to set the `height` and `width` of the container around the flow — the flow fills its parent, so without explicit dimensions it will not be visible. ## Controlled vs. Uncontrolled Flows Passing `nodes` and `edges` makes the flow **controlled**: your state is the source of truth, and the canvas renders exactly what the state contains. A controlled flow must wire `on_nodes_change` and `on_edges_change` as shown above, otherwise user interactions (dragging, selecting, deleting) are discarded. Passing only `default_nodes` and `default_edges` makes the flow **uncontrolled**: the component manages changes internally, but your state never learns about them. ## Where Changes Get Applied `rxe.flow.util.apply_node_changes` and `rxe.flow.util.apply_edge_changes` must be used inside of component code — in the lambda wired to `on_nodes_change` / `on_edges_change` — and never inside of state code. They evaluate on the client and produce the updated list, which is then passed to a plain setter event handler: ```python rxe.flow( ..., on_nodes_change=lambda changes: FlowState.set_nodes( rxe.flow.util.apply_node_changes(FlowState.nodes, changes) ), ) ``` Do not call these utilities from within an `@rx.event` handler; the state side should only receive and store the resulting list. # Nodes Source: http://localhost:3000/docs/enterprise/react-flow/nodes.md Nodes are the fundamental building blocks of a flow. This page explains how to define and customize nodes in Reflex Flow. ## The Node Type A node is represented as a Python dictionary with the following fields: - `id` (`str`) – Unique identifier for the node. - `position` (`dict`) – Position of the node with `x` and `y` coordinates. - `data` (`dict`) – Arbitrary data passed to the node component. - `type` (`str`) – Node type defined in `node_types`. - `sourcePosition` (`str`) – Controls source handle position ("top", "right", "bottom", "left"). - `targetPosition` (`str`) – Controls target handle position ("top", "right", "bottom", "left"). - `hidden` (`bool`) – Whether the node is visible on the canvas. - `selected` (`bool`) – Whether the node is currently selected. - `draggable` (`bool`) – Whether the node can be dragged. - `selectable` (`bool`) – Whether the node can be selected. - `connectable` (`bool`) – Whether the node can be connected to other nodes. - `deletable` (`bool`) – Whether the node can be deleted. - `width` (`float`) – Width of the node. - `height` (`float`) – Height of the node. - `parentId` (`str`) – Parent node ID for creating sub-flows. - `style` (`dict`) – Custom styles for the node. - `className` (`str`) – CSS class name for the node. ## Built-in Node Types Reflex Flow includes several built-in node types: ```python nodes: list[Node] = [ { "id": "1", "type": "input", "position": {"x": 100, "y": 100}, "data": {"label": "Start"}, }, { "id": "2", "type": "default", "position": {"x": 300, "y": 100}, "data": {"label": "Process"}, }, { "id": "3", "type": "output", "position": {"x": 500, "y": 100}, "data": {"label": "End"}, }, ] ``` - **input** – Entry point with only source handles - **default** – Standard node with both source and target handles - **output** – Exit point with only target handles ## Basic Node Configuration ### Node Positioning ```python node = { "id": "positioned-node", "type": "default", "position": {"x": 250, "y": 150}, "data": {"label": "Positioned Node"}, } ``` ### Node Styling ```python styled_node = { "id": "styled-node", "type": "default", "position": {"x": 100, "y": 200}, "data": {"label": "Custom Style"}, "style": { "background": "#ff6b6b", "color": "white", "border": "2px solid #ff5252", "borderRadius": "8px", "padding": "10px", }, } ``` ### Handle Positioning ```python node_with_handles = { "id": "handle-node", "type": "default", "position": {"x": 300, "y": 300}, "data": {"label": "Custom Handles"}, "sourcePosition": "right", "targetPosition": "left", } ``` ## Custom Nodes Creating custom nodes is as easy as building a regular React component and passing it to the `node_types`. Since they’re standard React components, you can display any content and implement any functionality you need. Plus, you’ll have access to a range of props that allow you to extend and customize the default node behavior. Below is an example custom node using a `color picker` component. ```python demo exec from typing import Any import reflex as rx import reflex_enterprise as rxe from reflex_enterprise.components.flow.types import Connection, Edge, Node class CustomNodeState(rx.State): bg_color: rx.Field[str] = rx.field(default="#c9f1dd") nodes: rx.Field[list[Node]] = rx.field( default_factory=lambda: [ { "id": "1", "type": "input", "data": {"label": "An input node"}, "position": {"x": 0, "y": 50}, "sourcePosition": "right", "style": { "color": "#000000", }, }, { "id": "2", "type": "selectorNode", "data": { "color": "#c9f1dd", }, "position": {"x": 300, "y": 50}, }, { "id": "3", "type": "output", "data": {"label": "Output A"}, "position": {"x": 650, "y": 25}, "targetPosition": "left", "style": { "color": "#000000", }, }, { "id": "4", "type": "output", "data": {"label": "Output B"}, "position": {"x": 650, "y": 100}, "targetPosition": "left", "style": { "color": "#000000", }, }, ] ) edges: rx.Field[list[Edge]] = rx.field( default_factory=lambda: [ { "id": "e1-2", "source": "1", "target": "2", "animated": True, }, { "id": "e2a-3", "source": "2", "target": "3", "animated": True, }, { "id": "e2b-4", "source": "2", "target": "4", "animated": True, }, ] ) @rx.event def on_change_color(self, color: str): self.nodes = [ node if node["id"] != "2" or "data" not in node else {**node, "data": {**node["data"], "color": color}} for node in self.nodes ] self.bg_color = color @rx.event def set_nodes(self, nodes: list[Node]): self.nodes = nodes @rx.event def set_edges(self, edges: list[Edge]): self.edges = edges @rx.memo def color_selector_node( data: rx.Var[dict], isConnectable: rx.Var[bool] ) -> rx.Component: data = data.to(dict) return rx.el.div( rxe.flow.handle( type="target", position="left", is_connectable=isConnectable, ), rx.el.div( "Custom Color Picker Node: ", rx.el.strong(data["color"]), ), rx.el.input( class_name="nodrag", type="color", on_change=CustomNodeState.on_change_color, default_value=data["color"], ), rxe.flow.handle( type="source", position="right", is_connectable=isConnectable, ), class_name="border border-1 p-2 rounded-sm", border_color=rx.color_mode_cond("black", ""), color="black", bg="white", ) def node_stroke_color(node: rx.vars.ObjectVar[Node]): return rx.match( node["type"], ("input", "#0041d0"), ( "selectorNode", CustomNodeState.bg_color, ), ("output", "#ff0072"), None, ) def node_color(node: rx.vars.ObjectVar[Node]): return rx.match( node["type"], ( "selectorNode", CustomNodeState.bg_color, ), "#fff", ) def custom_node(): return rx.box( rxe.flow( rxe.flow.background(bg_color=CustomNodeState.bg_color), rxe.flow.mini_map( node_stroke_color=rx.vars.function.ArgsFunctionOperation.create( ("node",), node_stroke_color(rx.Var("node").to(Node)) ), node_color=rx.vars.function.ArgsFunctionOperation.create( ("node",), node_color(rx.Var("node").to(Node)) ), ), rxe.flow.controls(), default_nodes=CustomNodeState.nodes, default_edges=CustomNodeState.edges, nodes=CustomNodeState.nodes, edges=CustomNodeState.edges, on_nodes_change=lambda changes: CustomNodeState.set_nodes( rxe.flow.util.apply_node_changes(CustomNodeState.nodes, changes) ), on_edges_change=lambda changes: CustomNodeState.set_edges( rxe.flow.util.apply_edge_changes(CustomNodeState.edges, changes) ), on_connect=lambda connection: CustomNodeState.set_edges( rxe.flow.util.add_edge( connection.to(dict).merge({"animated": True}).to(Connection), CustomNodeState.edges, ) ), node_types={"selectorNode": color_selector_node}, color_mode="light", snap_grid=(20, 20), default_viewport={"x": 0, "y": 0, "zoom": 1.5}, snap_to_grid=True, attribution_position="bottom-left", fit_view=True, ), height="100vh", width="100vw", ) ``` # Overview Source: http://localhost:3000/docs/enterprise/react-flow/overview.md At its core, a flow diagram is an interactive graph composed of nodes connected by edges. To help understand the key concepts, let’s go over the main components of a flow. ### Nodes Nodes are the building blocks of a flow. While there are a few default node types available, the real power comes from customizing them. You can design nodes to include interactive elements, display dynamic data, or support multiple connection points. The framework provides the foundation—you provide the functionality and style. ### Handles Handles are the points on a node where edges attach. They typically appear on the top, bottom, left, or right sides of a node, but can be positioned and styled freely. Nodes can have multiple handles, allowing for complex connection setups. ### Edges Edges are the connections between nodes. Each edge requires a source node and a target node. Edges can be styled and customized, and nodes with multiple handles can support multiple edges. Custom edges can include interactive elements, specialized routing, or unique visual styles beyond simple lines. ### Connection Line When creating a new edge, you can click and drag from one handle to another. While dragging, the placeholder edge is called a connection line. Connection lines behave like edges and can be customized in appearance and behavior. ### Viewport The viewport is the visible area containing the flow. Each node has x- and y-coordinates representing its position. Moving the viewport changes these coordinates, and zooming in or out adjusts the zoom level. The viewport ensures the diagram remains navigable and interactive. # Theming Source: http://localhost:3000/docs/enterprise/react-flow/theming.md You can customize the appearance of the Flow component using CSS. The Flow component comes with a default theme, which you can override with your own styles. ## CSS Variables The Flow component uses CSS variables for theming. You can override these variables to change the appearance of the flow. Here are some of the most common variables: ```css .react-flow { --xy-background-color: #f7f9fb; --xy-node-border-default: 1px solid #ededed; --xy-node-boxshadow-default: 0px 3.54px 4.55px 0px #00000005, 0px 3.54px 4.55px 0px #0000000d, 0px 0.51px 1.01px 0px #0000001a; --xy-node-border-radius-default: 8px; --xy-handle-background-color-default: #ffffff; --xy-handle-border-color-default: #aaaaaa; --xy-edge-label-color-default: #505050; } ``` ## Custom Stylesheets You can add custom stylesheets to your app to override the default styles. To do this, add the `stylesheets` prop to your `rxe.App` or `rx.App` instance: ```python app = rxe.App( stylesheets=[ "/css/my-custom-styles.css", ], ) ``` Then, create a file `assets/css/my-custom-styles.css` in your project and add your custom styles there. ## Customizing Node and Edge Styles You can also apply custom styles to individual nodes and edges using the `style` and `className` props. ### Using the style prop You can pass a style dictionary to the `style` prop of a node or edge: ```python node = { "id": "1", "position": {"x": 100, "y": 100}, "data": {"label": "Node 1"}, "style": {"backgroundColor": "#ffcc00"}, } ``` ### Using the className prop You can also pass a class name to the `className` prop and define the styles in your CSS file: ```python # In your python code node = { "id": "1", "position": {"x": 100, "y": 100}, "data": {"label": "Node 1"}, "className": "my-custom-node", } ``` ```css /* In your CSS file */ .my-custom-node { background-color: #ffcc00; border: 2px solid #ff9900; border-radius: 10px; } ``` # Utility Functions Source: http://localhost:3000/docs/enterprise/react-flow/utils.md The `rxe.flow.util` module provides utility functions for working with Flow components. ## Path Utilities These functions are used to calculate the path for an edge. - `get_simple_bezier_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top")`: Returns everything you need to render a simple bezier edge between two nodes. - `get_bezier_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top", curvature=0.5)`: Returns everything you need to render a bezier edge between two nodes. - `get_straight_path(source_x, source_y, target_x, target_y)`: Calculates the straight line path between two points. - `get_smooth_step_path(source_x, source_y, target_x, target_y, source_position="bottom", target_position="top", border_radius=5, center_x=None, center_y=None, offset=20, step_position=0.5)`: Returns everything you need to render a stepped path between two nodes. ## Change Handlers These functions are used to apply changes to nodes and edges from the `on_nodes_change` and `on_edges_change` event handlers. - `apply_node_changes(nodes, changes)`: Applies changes to nodes in the flow. - `apply_edge_changes(edges, changes)`: Applies changes to edges in the flow. ## Edge and Connection Utilities - `add_edge(params, edges)`: Creates a new edge in the flow. ## Graph Utilities - `get_incomers(node_id, nodes, edges)`: Returns all incoming nodes connected to the given node. - `get_outgoers(node_id, nodes, edges)`: Returns all outgoing nodes connected to the given node. - `get_connected_edges(nodes, edges)`: Returns all edges connected to the given nodes. # Single Port Proxy Source: http://localhost:3000/docs/enterprise/single-port-proxy.md Enable single-port deployment by proxying the backend to the frontend port. ## Configuration ```python import reflex_enterprise as rxe config = rxe.Config( use_single_port=True, ) ``` This allows your application to run on a single port, which is useful for deployment scenarios where you can only expose one port. # End-to-End Testing Source: http://localhost:3000/docs/enterprise/testing.md --- title: End-to-End Testing --- _New in reflex-enterprise v0.9.2._ reflex-enterprise ships a pytest plugin that starts an existing Reflex app with `reflex run` and passes the live URL to tests. Tests drive the running app — frontend and backend — with browser automation such as [pytest-playwright](https://playwright.dev/python/docs/test-runners). The plugin manages the app process and returns its URL. Browser automation is chosen and controlled by the test suite. ## Installation ```bash uv add "reflex-enterprise[testing]>=0.9.2" uv add pytest-playwright # browser driver of your choice uv run playwright install chromium ``` The `[testing]` extra installs `pytest`, but does not include a browser driver. `pytest-playwright` is used in the examples below, but any browser automation framework that can drive a live URL is compatible. The fixture is registered through a `pytest11` entry point, so no `conftest.py` configuration is required. ## Quickstart Place a test anywhere at or below the directory containing `rxconfig.py`: ```text my_app/ rxconfig.py my_app/ my_app.py tests/ test_homepage.py ``` ```python from playwright.sync_api import Page, expect from reflex_enterprise.testing import ReflexApp def test_homepage(reflex_app: ReflexApp, page: Page): page.goto(reflex_app.url) expect(page.get_by_role("heading", name="Welcome")).to_be_visible() ``` Run it: ```bash uv run pytest ``` The first test that requests `reflex_app` starts the app. A cold start compiles the frontend; subsequent runs reuse a cached working directory and start quickly. The running app is shared by every test in the session and shut down when the session ends. ```md alert warning # Shared app process, isolated per-test state. The app process stays alive across test cases, but the state does not carry over between them. Each browser context connects with its own `client_token`, and Reflex keys backend state by that token, so every test that opens a fresh `page` (the function-scoped `pytest-playwright` fixture) talks to a clean, independent state instance. This means tests never see each other's state, but it also means nothing a test does in the UI — logging in, navigating, opening a dialog — persists into the next test. Reproduce any required starting condition per test (see [Reusing setup steps](#reusing-setup-steps)). Reusing one `page`/context across tests would share a `client_token` and leak state between them; keep to one context per test. ``` ## How it works - **Discovery** — the app under test is found by walking up from the test file to the nearest `rxconfig.py`. That directory is the app root. - **Isolation** — the app runs with relocated `REFLEX_WEB_WORKDIR` / `REFLEX_STATES_WORKDIR`, telemetry disabled, in a working directory outside the checkout, so compiled frontend and state artifacts never land in the working tree. `reflex run` itself may still write a `reflex.lock` next to `rxconfig.py`, you may `.gitignore` it for pure test apps. - **Reuse and bounding** — a session-scoped manager caches running apps by app root and run mode. It will reuse healthy running apps keeping at most `max_apps` alive, and evicting the least recently used to make room for a different app. The `reflex_app` fixture is function-scoped but borrows from the manager, so apps outlive individual tests. - **Ports** — `reflex run` picks its own ports (auto-incrementing from 3000/8000) and the plugin reads the real URL back from its output. - **Readiness** — the fixture is fulfilled only when the frontend and the backend are both accepting connections. If either fails to come up, the fixture raises at setup with the captured `reflex run` output in the message, and the test is reported as an error. The same contract governs reuse: a cached app whose frontend or backend is no longer reachable is torn down and restarted before being handed to a test. ## The `reflex_app` fixture The `reflex_app` fixture returns a `ReflexApp` object with a small, stable surface: | Property | Description | |---|---| | `reflex_app.url` | Live frontend URL (no trailing slash) — pass to `page.goto(...)`. | | `reflex_app.backend_url` | Live backend URL, if available. | | `reflex_app.app_root` | The resolved app root (directory containing `rxconfig.py`). | | `reflex_app.logs()` | Captured `reflex run` output (stdout and stderr merged, most recent lines). | ## Reusing setup steps Because every test starts from a clean state (see the warning above), any starting condition a test depends on — a logged-in session, a populated form, an open dialog — has to be re-established for that test. Factor those steps into a function-scoped fixture so each test body can assume the app is already in the state it cares about. For example, a fixture that clicks through a menu to open a settings modal, so the test starts with the modal already open: ```python import pytest from playwright.sync_api import Page, expect from reflex_enterprise.testing import ReflexApp @pytest.fixture def settings_modal(reflex_app: ReflexApp, page: Page) -> Page: """Open the settings modal and hand the test a page where it is visible.""" page.goto(reflex_app.url) page.get_by_role("button", name="Menu").click() page.get_by_role("menuitem", name="Settings").click() expect(page.get_by_role("dialog", name="Settings")).to_be_visible() return page def test_change_theme(settings_modal: Page): # The modal is already open; go straight to the assertion. settings_modal.get_by_label("Dark mode").check() expect(settings_modal.get_by_label("Dark mode")).to_be_checked() ``` The fixture depends on `reflex_app` and `page`, both function-scoped, so it runs once per test against that test's own `client_token` — the setup is repeated for every test but the state never leaks between them. Fixtures like this compose: a `logged_in` fixture can navigate from the login page, and `settings_modal` can depend on it to open the modal as an authenticated user. ## Debugging failures A frontend assertion can fail because of a server-side problem — a startup warning, a compile error, a backend traceback during an event. The plugin surfaces the server output in three places: - **When a test fails**, the captured `reflex run` output of every app the test borrowed is attached to the report as a section, printed alongside the failure like captured stdout: ```text ------- captured `reflex run` output (my_app) ------- ... App running at: http://localhost:3000 ERROR: Traceback (most recent call last): ... ``` - **When the app fails to start**, the `ReflexAppStartError` raised during fixture setup embeds the full captured output, so the cause (bad rxconfig, missing dependency, port conflict) is part of the error. - **Programmatic access** — `reflex_app.logs()` returns the same captured output for custom assertions or logging. ## Configuration Every option can be set via a command-line flag, a pytest ini option, or an environment variable. Precedence is CLI > env > ini > default. | Setting | CLI | ini | env var | Default | |---|---|---|---|---| | Max running apps | `--reflex-max-apps` | `reflex_max_apps` | `REFLEX_TEST_MAX_APPS` | `1` | | Run mode(s) | `--reflex-run-mode` | `reflex_run_mode` | `REFLEX_TEST_RUN_MODE` | `dev` | | Workdir strategy | `--reflex-workdir-strategy` | `reflex_workdir_strategy` | `REFLEX_TEST_WORKDIR_STRATEGY` | `persistent` | | Workdir root | `--reflex-workdir-root` | `reflex_workdir_root` | `REFLEX_TEST_WORKDIR_ROOT` | `/reflex-enterprise-testing` | | Start timeout (s) | `--reflex-start-timeout` | `reflex_start_timeout` | `REFLEX_TEST_START_TIMEOUT` | `300` | | Extra `reflex run` args | `--reflex-run-arg` (repeatable) | `reflex_run_args` | `REFLEX_TEST_RUN_ARGS` | *(none)* | | Extra subprocess env | `--reflex-run-env` (repeatable) | `reflex_run_env` | `REFLEX_TEST_RUN_ENV` | *(none)* | | Share/isolate `REFLEX_DIR` | `--reflex-share-reflex-dir` / `--reflex-isolate-reflex-dir` | `reflex_share_reflex_dir` | `REFLEX_TEST_SHARE_REFLEX_DIR` | shared | - **`run_mode`** — the `reflex run` env mode: `dev` (default), `prod`, or a comma-separated list (`--reflex-run-mode=dev,prod`). With more than one mode, every test that uses `reflex_app` is parameterized to run once per mode, and tests are grouped so all of one mode run before the next mode starts. Dev and prod instances of the same app are cached separately (each counts toward `max_apps`) with separate persistent workdirs, so their build outputs never mix. Tests can read the current mode via the session-scoped `reflex_run_mode` fixture (`"dev"` or `"prod"`). In `prod` mode reflex serves the frontend and backend on a single address, so `reflex_app.backend_url` falls back to `reflex_app.url`. - **`workdir_strategy`** — `persistent` reuses a per-app, per-run-mode cache directory across sessions (warm starts, roughly 6x faster than cold); `tmp` uses a fresh temporary directory each start (clean but always cold). - **`reflex_run_args`** — extra CLI arguments appended to `reflex run`. `--frontend-only` / `--backend-only` are rejected immediately, since a partial run can never satisfy the readiness check. The plugin appends its own `--env ` after these args, so an `--env` here is overridden — set the mode with `--reflex-run-mode` instead. - **Share/isolate `REFLEX_DIR`** — by default the global bun/node/reflex dependencies are shared with the host to avoid a slow re-download. Pass `--reflex-isolate-reflex-dir` for a fully hermetic (slower) run, or `--reflex-share-reflex-dir` to force sharing over an ini/env setting (CLI wins either way). Example `pyproject.toml`: ```toml [tool.pytest.ini_options] reflex_max_apps = "2" reflex_workdir_strategy = "persistent" reflex_start_timeout = "180" ``` ### Tuning settings from a fixture For anything the static options can't express, the session-scoped `reflex_app_manager` fixture returns the `AppManager`, whose `settings` attribute (a `ReflexAppTestSettings`) is the supported integration point — mutate it from your own fixture. Settings are consumed when an app starts, so apply changes before the first `reflex_app` use, e.g. in an autouse session fixture: ```python import pytest from reflex_enterprise.testing import AppManager @pytest.fixture(scope="session", autouse=True) def _reflex_settings(reflex_app_manager: AppManager) -> None: reflex_app_manager.settings.start_timeout = 120.0 reflex_app_manager.settings.extra_env = {"MY_FEATURE_FLAG": "1"} ``` `ReflexAppTestSettings` is a dataclass holding the resolved value of every option from the table above (CLI/env/ini already applied), plus the same defaults when unset: ```python @dataclass class ReflexAppTestSettings: max_apps: int = 1 run_modes: tuple[str, ...] = ("dev",) workdir_strategy: Literal["persistent", "tmp"] = "persistent" workdir_root: Path = ... start_timeout: float = 300.0 # seconds reflex_run_args: tuple[str, ...] = () extra_env: Mapping[str, str] = field(default_factory=dict) share_reflex_dir: bool = True ``` `AppManager`, `ReflexApp`, and `ReflexAppTestSettings` are all importable from `reflex_enterprise.testing` for proper typing of fixtures like the one above. ## Measuring coverage Because the app code executes in the `reflex run` subprocess started by the fixture, pytest itself may not import or execute any app code directly. To record coverage requires enabling coverage.py's `subprocess` patch to measure the child process data and combine it into the final report. Configure it in `pyproject.toml` — for an app named `my_app`: ```toml [tool.coverage.run] # Measure the app code explicitly since it may not be directly imported in the # main pytest process. source = ["my_app"] # a subprocess will write a separate .coverage.* file to be combined by pytest-cov. parallel = true patch = [ "subprocess", # inject coverage into `reflex run` and workers "_exit", # flush coverage data on os._exit() ] # The test harness stops the app with SIGTERM. sigterm = true # Ignore warning if the test cases never imports `my_app` directly. disable_warnings = ["no-data-collected"] ``` Then run with [pytest-cov](https://pytest-cov.readthedocs.io/): ```bash uv add "pytest-cov>=7.1" uv run pytest --cov ``` Each app subprocess writes its own `.coverage.*` data file; pytest-cov combines them when the session ends and reports coverage for the app package. ## Notes - **pytest-xdist** — each worker process gets its own manager, so the effective cap on running apps is `max_apps * num_workers`. - **Concurrent `pytest` runs** — in `persistent` mode each app's workdir carries a pid lockfile. If another `pytest` process is already using an app's workdir, a second run transparently falls back to its own throwaway workdir so the two never recompile or clobber the same `.web`. Stale locks (from a crashed run) are reclaimed automatically. - **`frontend_path`** — `reflex_app.url` honors a configured `frontend_path` (it is read from reflex's own "App running at:" line). - The plugin starts apps one at a time and waits for readiness, which is required for `reflex run`'s port auto-increment to work correctly when more than one app runs. # Background Tasks Source: http://localhost:3000/docs/events/background-events.md ```python exec import reflex as rx ``` A background task is a special type of `EventHandler` that may run concurrently with other `EventHandler` functions. This enables long-running tasks to execute without blocking UI interactivity. A background task is defined by decorating an async `State` method with `@rx.event(background=True)`. ```md alert warning # `@rx.event(background=True)` used to be called `@rx.background`. In Reflex version 0.6.5 and later, the `@rx.background` decorator has been renamed to `@rx.event(background=True)`. ``` Whenever a background task needs to interact with the state, **it must enter an `async with self` context block** which refreshes the state and takes an exclusive lock to prevent other tasks or event handlers from modifying it concurrently. Because other `EventHandler` functions may modify state while the task is running, **outside of the context block, Vars accessed by the background task may be _stale_**. Attempting to modify the state from a background task outside of the context block will raise an `ImmutableStateError` exception. In the following example, the `my_task` event handler is decorated with `@rx.event(background=True)` and increments the `counter` variable every half second, as long as certain conditions are met. While it is running, the UI remains interactive and continues to process events normally. ```md alert info # Background events are similar to simple Task Queues like [Celery](https://www.fullstackpython.com/celery.html) allowing asynchronous events. ``` ```python demo exec id=background_demo import asyncio import reflex as rx class MyTaskState(rx.State): counter: int = 0 max_counter: int = 10 running: bool = False _n_tasks: int = 0 @rx.event def set_max_counter(self, value: str): self.max_counter = int(value) @rx.event(background=True) async def my_task(self): async with self: # The latest state values are always available inside the context if self._n_tasks > 0: # only allow 1 concurrent task return # State mutation is only allowed inside context block self._n_tasks += 1 while True: async with self: # Check for stopping conditions inside context if self.counter >= self.max_counter: self.running = False if not self.running: self._n_tasks -= 1 return self.counter += 1 # Await long operations outside the context to avoid blocking UI await asyncio.sleep(0.5) @rx.event def toggle_running(self): self.running = not self.running if self.running: return MyTaskState.my_task @rx.event def clear_counter(self): self.counter = 0 def background_task_example(): return rx.hstack( rx.heading(MyTaskState.counter, " /", as_="h2"), rx.input( value=MyTaskState.max_counter, on_change=MyTaskState.set_max_counter, width="8em", ), rx.button( rx.cond(~MyTaskState.running, "Start", "Stop"), on_click=MyTaskState.toggle_running, ), rx.button( "Reset", on_click=MyTaskState.clear_counter, ), ) ``` ## Terminating Background Tasks on Page Close or Navigation Sometimes, background tasks may continue running even after the user navigates away from the page or closes the browser tab. To handle such cases, you can check if the websocket associated with the state is disconnected and terminate the background task when necessary. The solution involves checking if the client_token is still valid in the app.event_namespace.token_to_sid mapping. If the session is lost (e.g., the user navigates away or closes the page), the background task will stop. ```python import asyncio import reflex as rx class State(rx.State): @rx.event(background=True) async def loop_function(self): while True: if self.router.session.client_token not in app.event_namespace.token_to_sid: print( "WebSocket connection closed or user navigated away. Stopping background task." ) break print("Running background task...") await asyncio.sleep(2) @rx.page(on_load=State.loop_function) def index(): return rx.text( "Hello, this page will manage background tasks and stop them when the page is closed or navigated away." ) ``` ## Task Lifecycle When a background task is triggered, it starts immediately, saving a reference to the task in `app.background_tasks`. When the task completes, it is removed from the set. Multiple instances of the same background task may run concurrently, and the framework makes no attempt to avoid duplicate tasks from starting. It is up to the developer to ensure that duplicate tasks are not created under the circumstances that are undesirable. In the example above, the `_n_tasks` backend var is used to control whether `my_task` will enter the increment loop, or exit early. ## Background Task Limitations Background tasks mostly work like normal `EventHandler` methods, with certain exceptions: - Background tasks must be `async` functions. - Background tasks cannot modify the state outside of an `async with self` context block. - Background tasks may read the state outside of an `async with self` context block, but the value may be stale. - Background tasks may not be directly called from other event handlers or background tasks. Instead use `yield` or `return` to trigger the background task. # Chaining events Source: http://localhost:3000/docs/events/chaining-events.md ```python exec import reflex as rx ``` ## Calling Event Handlers From Event Handlers You can call other event handlers from event handlers to keep your code modular. Just use the `self.call_handler` syntax to run another event handler. As always, you can yield within your function to send incremental updates to the frontend. ```python demo exec id=call-handler import asyncio class CallHandlerState(rx.State): count: int = 0 progress: int = 0 @rx.event def set_progress(self, value: int): self.progress = value @rx.event async def run(self): # Reset the count. self.set_progress(0) yield # Count to 10 while showing progress. for i in range(10): # Wait and increment. await asyncio.sleep(0.5) self.count += 1 # Update the progress. self.set_progress(i + 1) # Yield to send the update. yield def call_handler_example(): return rx.vstack( rx.badge(CallHandlerState.count, font_size="1.5em", color_scheme="green"), rx.progress(value=CallHandlerState.progress, max=10, width="100%"), rx.button("Run", on_click=CallHandlerState.run), ) ``` ## Returning Events From Event Handlers So far, we have only seen events that are triggered by components. However, an event handler can also return events. In Reflex, event handlers run synchronously, so only one event handler can run at a time, and the events in the queue will be blocked until the current event handler finishes.The difference between returning an event and calling an event handler is that returning an event will send the event to the frontend and unblock the queue. ```md alert info # Be sure to use the class name `State` (or any substate) rather than `self` when returning events. ``` Try entering an integer in the input below then clicking out. ```python demo exec id=collatz class CollatzState(rx.State): count: int = 1 @rx.event def start_collatz(self, count: str): """Run the collatz conjecture on the given number.""" self.count = abs(int(count if count else 1)) return CollatzState.run_step @rx.event async def run_step(self): """Run a single step of the collatz conjecture.""" while self.count > 1: await asyncio.sleep(0.5) if self.count % 2 == 0: # If the number is even, divide by 2. self.count //= 2 else: # If the number is odd, multiply by 3 and add 1. self.count = self.count * 3 + 1 yield def collatz_example(): return rx.vstack( rx.badge(CollatzState.count, font_size="1.5em", color_scheme="green"), rx.input(on_blur=CollatzState.start_collatz), ) ``` In this example, we run the [Collatz Conjecture](https://en.wikipedia.org/wiki/Collatz_conjecture) on a number entered by the user. When the `on_blur` event is triggered, the event handler `start_collatz` is called. It sets the initial count, then calls `run_step` which runs until the count reaches `1`. # Decentralized Event Handlers Source: http://localhost:3000/docs/events/decentralized-event-handlers.md ```python exec import reflex as rx ``` ## Overview Decentralized event handlers allow you to define event handlers outside of state classes, providing more flexible code organization. This feature was introduced in Reflex v0.7.10 and enables a more modular approach to event handling. With decentralized event handlers, you can: - Organize event handlers by feature rather than by state class - Separate UI logic from state management - Create more maintainable and scalable applications ## Basic Usage To create a decentralized event handler, use the `@rx.event` decorator on a function that takes a state instance as its first parameter: ```python demo exec import reflex as rx class MyState(rx.State): count: int = 0 @rx.event def increment(state: MyState, amount: int): state.count += amount def decentralized_event_example(): return rx.vstack( rx.heading(f"Count: {MyState.count}", as_="h2"), rx.hstack( rx.button("Increment by 1", on_click=increment(1)), rx.button("Increment by 5", on_click=increment(5)), rx.button("Increment by 10", on_click=increment(10)), ), spacing="4", align="center", ) ``` In this example: 1. We define a `MyState` class with a `count` variable 2. We create a decentralized event handler `increment` that takes a `MyState` instance as its first parameter 3. We use the event handler in buttons, passing different amounts to increment by ## Compared to Traditional Event Handlers Here's a comparison between traditional event handlers defined within state classes and decentralized event handlers: ```python box # Traditional event handler within a state class class TraditionalState(rx.State): count: int = 0 @rx.event def increment(self, amount: int = 1): self.count += amount # Usage in components rx.button("Increment", on_click=TraditionalState.increment(5)) # Decentralized event handler outside the state class class DecentralizedState(rx.State): count: int = 0 @rx.event def increment(state: DecentralizedState, amount: int = 1): state.count += amount # Usage in components rx.button("Increment", on_click=increment(5)) ``` Key differences: - Traditional event handlers use `self` to reference the state instance - Decentralized event handlers explicitly take a state instance as the first parameter - Both approaches use the same syntax for triggering events in components - Both can be decorated with `@rx.event` respectively ## Best Practices ### When to Use Decentralized Event Handlers Decentralized event handlers are particularly useful in these scenarios: 1. **Large applications** with many event handlers that benefit from better organization 2. **Feature-based organization** where you want to group related event handlers together 3. **Separation of concerns** when you want to keep state definitions clean and focused ### Type Annotations Always use proper type annotations for your state parameter and any additional parameters: ```python box @rx.event def update_user(state: UserState, name: str, age: int): state.name = name state.age = age ``` ### Naming Conventions Follow these naming conventions for clarity: 1. Use descriptive names that indicate the action being performed 2. Use the state class name as the type annotation for the first parameter 3. Name the state parameter consistently across your codebase (e.g., always use `state` or the first letter of the state class) ### Organization Consider these approaches for organizing decentralized event handlers: 1. Group related event handlers in the same file 2. Place event handlers near the state classes they modify 3. For larger applications, create a dedicated `events` directory with files organized by feature ```python box # Example organization in a larger application # events/user_events.py @rx.event def update_user(state: UserState, name: str, age: int): state.name = name state.age = age @rx.event def delete_user(state: UserState): state.name = "" state.age = 0 ``` ### Combining with Other Event Features Decentralized event handlers work seamlessly with other Reflex event features: ```python box # Background event @rx.event(background=True) async def long_running_task(state: AppState): # Long-running task implementation pass # Event chaining @rx.event def process_form(state: FormState, data: dict): # Process form data return validate_data # Chain to another event ``` # Event Actions Source: http://localhost:3000/docs/events/event-actions.md ```python exec import reflex as rx import datetime ``` In Reflex, an event action is a special behavior that occurs during or after processing an event on the frontend. Event actions can modify how the browser handles DOM events or throttle and debounce events before they are processed by the backend. An event action is specified by accessing attributes and methods present on all EventHandlers and EventSpecs. ## DOM Event Propagation _Added in v0.3.2_ ### prevent_default The `.prevent_default` action prevents the default behavior of the browser for the action. This action can be added to any existing event, or it can be used on its own by specifying `rx.prevent_default` as an event handler. A common use case for this is to prevent navigation when clicking a link. ```python demo rx.link( "This Link Does Nothing", href="https://reflex.dev/", on_click=rx.prevent_default ) ``` ```python demo exec class LinkPreventDefaultState(rx.State): status: bool = False @rx.event def toggle_status(self): self.status = not self.status def prevent_default_example(): return rx.vstack( rx.heading(f"The value is {LinkPreventDefaultState.status}", as_="h2"), rx.link( "Toggle Value", href="https://reflex.dev/", on_click=LinkPreventDefaultState.toggle_status.prevent_default, ), ) ``` ### stop_propagation The `.stop_propagation` action stops the event from propagating to parent elements. This action is often used when a clickable element contains nested buttons that should not trigger the parent element's click event. In the following example, the first button uses `.stop_propagation` to prevent the click event from propagating to the outer vstack. The second button does not use `.stop_propagation`, so the click event will also be handled by the on_click attached to the outer vstack. ```python demo exec class StopPropagationState(rx.State): where_clicked: list[str] = [] @rx.event def handle_click(self, where: str): self.where_clicked.append(where) @rx.event def handle_reset(self): self.where_clicked = [] def stop_propagation_example(): return rx.vstack( rx.button( "btn1 - Stop Propagation", on_click=StopPropagationState.handle_click("btn1").stop_propagation, ), rx.button( "btn2 - Normal Propagation", on_click=StopPropagationState.handle_click("btn2"), ), rx.foreach(StopPropagationState.where_clicked, rx.text), rx.button( "Reset", on_click=StopPropagationState.handle_reset.stop_propagation, ), padding="2em", border=f"1px dashed {rx.color('accent', 5)}", on_click=StopPropagationState.handle_click("outer"), ) ``` ## Throttling and Debounce _Added in v0.5.0_ For events that are fired frequently, it can be useful to throttle or debounce them to avoid network latency and improve performance. These actions both take a single argument which specifies the delay time in milliseconds. ### throttle The `.throttle` action limits the number of times an event is processed within a a given time period. It is useful for `on_scroll` and `on_mouse_move` events which are fired very frequently, causing lag when handling them in the backend. ```md alert warning # Throttled events are discarded. There is no eventual delivery of any event that is triggered while the throttle period is active. Throttle is not appropriate for events when the final payload contains data that must be processed, like `on_change`. ``` In the following example, the `on_scroll` event is throttled to only fire every half second. ```python demo exec class ThrottleState(rx.State): last_scroll: datetime.datetime | None @rx.event def handle_scroll(self): self.last_scroll = datetime.datetime.now(datetime.timezone.utc) def scroll_box(): return rx.scroll_area( rx.heading("Scroll Me", as_="h2"), *[rx.text(f"Item {i}") for i in range(100)], height="75px", width="50%", border=f"1px solid {rx.color('accent', 5)}", on_scroll=ThrottleState.handle_scroll.throttle(500), ) def throttle_example(): return ( scroll_box(), rx.text( "Last Scroll Event: ", rx.moment(ThrottleState.last_scroll, format="HH:mm:ss.SSS"), ), ) ``` ```md alert info # Event Actions are Chainable Event actions can be chained together to create more complex event handling behavior. For example, you can throttle an event and prevent its default behavior in the same event handler: `on_click=MyState.handle_click.throttle(500).prevent_default`. ``` ### debounce The `.debounce` action delays the processing of an event until the specified timeout occurs. If another event is triggered during the timeout, the timer is reset and the original event is discarded. Debounce is useful for handling the final result of a series of events, such as moving a slider. ```md alert warning # Debounced events are discarded. When a new event is triggered during the debounce period, the original event is discarded. Debounce is not appropriate for events where each payload contains unique data that must be processed, like `on_key_down`. ``` In the following example, the slider's `on_change` handler, `update_value`, is only triggered on the backend when the slider value has not changed for half a second. ```python demo exec class DebounceState(rx.State): settled_value: int = 50 @rx.event def update_value(self, value: list[int | float]): self.settled_value = value[0] def debounced_slider(): return rx.slider( key=rx.State.router.session.session_id, default_value=[DebounceState.settled_value], on_change=DebounceState.update_value.debounce(500), width="100%", ) def debounce_example(): return rx.vstack( debounced_slider(), rx.text(f"Settled Value: {DebounceState.settled_value}"), ) ``` ```md alert info # Why set key on the slider? Setting `key` to the `session_id` with a dynamic `default_value` ensures that when the page is refreshed, the component will be re-rendered to reflect the updated default_value from the state. Without the `key` set, the slider would always display the original `settled_value` after a page reload, instead of its current value. ``` ## Temporal Events _Added in [v0.6.6](https://github.com/reflex-dev/reflex/releases/tag/v0.6.6)_ ### temporal The `.temporal` action prevents events from being queued when the backend is down. This is useful for non-critical events where you do not want them to pile up if there is a temporary connection issue. ```md alert warning # Temporal events are discarded when the backend is down. When the backend is unavailable, events with the `.temporal` action will be discarded rather than queued for later processing. Only use this for events where it is acceptable to lose some interactions during connection issues. ``` In the following example, the `rx.moment` component with `interval` and `on_change` uses `.temporal` to prevent periodic updates from being queued when the backend is down: ```python demo exec class TemporalState(rx.State): current_time: str = "" @rx.event def update_time(self): self.current_time = datetime.datetime.now().strftime("%H:%M:%S") def temporal_example(): return rx.vstack( rx.heading("Current Time:", as_="h2"), rx.heading(TemporalState.current_time, as_="h2"), rx.moment( interval=1000, on_change=TemporalState.update_time.temporal, ), rx.text("Time updates will not be queued if the backend is down."), ) ``` # Event Arguments Source: http://localhost:3000/docs/events/event-arguments.md ```python exec import reflex as rx ``` The event handler signature needs to match the event trigger definition argument count. If the event handler takes two arguments, the event trigger must be able to provide two arguments. Here is a simple example: ```python demo exec class EventArgStateSlider(rx.State): value: int = 50 @rx.event def set_end(self, value: list[int | float]): self.value = value[0] def slider_max_min_step(): return rx.vstack( rx.heading(EventArgStateSlider.value, as_="h2"), rx.slider( default_value=40, on_value_commit=EventArgStateSlider.set_end, ), width="100%", ) ``` The event trigger here is `on_value_commit` and it is called when the value changes at the end of an interaction. This event trigger passes one argument, which is the value of the slider. The event handler which is triggered by the event trigger must therefore take one argument, which is `value` here. Here is a form example: ```python demo exec class EventArgState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): """Handle the form submit.""" self.form_data = form_data def event_arg_example(): return rx.vstack( rx.form( rx.vstack( rx.input( placeholder="Name", name="name", ), rx.checkbox("Checked", name="check"), rx.button("Submit", type="submit"), ), on_submit=EventArgState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.heading("Results", as_="h2"), rx.text(EventArgState.form_data.to_string()), ) ``` In this example the event trigger is the `on_submit` event of the form. The event handler is `handle_submit`. The `on_submit` event trigger passes one argument, the form data as a dictionary, to the `handle_submit` event handler. The `handle_submit` event handler must take one argument because the `on_submit` event trigger passes one argument. When the number of args accepted by an EventHandler differs from that provided by the event trigger, an `EventHandlerArgMismatch` error will be raised. ## Pass Additional Arguments to Event Handlers In some use cases, you want to pass additional arguments to your event handlers. To do this you can bind an event trigger to a lambda, which can call your event handler with the arguments you want. Try typing a color in an input below and clicking away from it to change the color of the input. ```python demo exec class ArgState(rx.State): colors: list[str] = ["rgba(245,168,152)", "MediumSeaGreen", "#DEADE3"] @rx.event def change_color(self, color: str, index: int): self.colors[index] = color def event_arguments_example(): return rx.hstack( rx.input( default_value=ArgState.colors[0], on_blur=lambda c: ArgState.change_color(c, 0), bg=ArgState.colors[0], ), rx.input( default_value=ArgState.colors[1], on_blur=lambda c: ArgState.change_color(c, 1), bg=ArgState.colors[1], ), rx.input( default_value=ArgState.colors[2], on_blur=lambda c: ArgState.change_color(c, 2), bg=ArgState.colors[2], ), ) ``` In this case, in we want to pass two arguments to the event handler `change_color`, the color and the index of the color to change. The `on_blur` event trigger passes the text of the input as an argument to the lambda, and the lambda calls the `change_color` event handler with the text and the index of the input. When the number of args accepted by a lambda differs from that provided by the event trigger, an `EventFnArgMismatch` error will be raised. ```md alert warning # Event Handler Parameters should provide type annotations. Like state vars, be sure to provide the right type annotations for the parameters in an event handler. ``` ## Events with Partial Arguments (Advanced) _Added in v0.5.0_ Event arguments in Reflex are passed positionally. Any additional arguments not passed to an EventHandler will be filled in by the event trigger when it is fired. The following two code samples are equivalent: ```python # Use a lambda to pass event trigger args to the EventHandler. rx.text(on_blur=lambda v: MyState.handle_update("field1", v)) # Create a partial that passes event trigger args for any args not provided to the EventHandler. rx.text(on_blur=MyState.handle_update("field1")) ``` # Events Overview Source: http://localhost:3000/docs/events/events-overview.md ```python exec import reflex as rx ``` Events are composed of two parts: Event Triggers and Event Handlers. - **Events Handlers** are how the State of a Reflex application is updated. They are triggered by user interactions with the UI, such as clicking a button or hovering over an element. Events can also be triggered by the page loading or by other events. - **Event triggers** are component props that create an event to be sent to an event handler. Each component supports a set of events triggers. They are described in each [component's documentation](/docs/library) in the event trigger section. ## Example Lets take a look at an example below. Try mousing over the heading to change the word. ```python demo exec class WordCycleState(rx.State): # The words to cycle through. text: list[str] = ["Welcome", "to", "Reflex", "!"] # The index of the current word. index: int = 0 @rx.event def next_word(self): self.index = (self.index + 1) % len(self.text) @rx.var def get_text(self) -> str: return self.text[self.index] def event_triggers_example(): return rx.heading( WordCycleState.get_text, as_="h2", on_mouse_over=WordCycleState.next_word, color="green", ) ``` In this example, the heading component has the **event trigger**, `on_mouse_over`. Whenever the user hovers over the heading, the `next_word` **event handler** will be called to cycle the word. Once the handler returns, the UI will be updated to reflect the new state. Adding the `@rx.event` decorator above the event handler is strongly recommended. This decorator enables proper static type checking, which ensures event handlers receive the correct number and types of arguments. ## What's in this section? In the event section of the documentation, you will explore the different types of events supported by Reflex, along with the different ways to call them. # Page Load Events Source: http://localhost:3000/docs/events/page-load-events.md ```python exec import reflex as rx ``` You can also specify a function to run when the page loads. This can be useful for fetching data once vs on every render or state change. In this example, we fetch data when the page loads: ```python class State(rx.State): data: Dict[str, Any] @rx.event def get_data(self): # Fetch data self.data = fetch_data() @rx.page(on_load=State.get_data) def index(): return rx.text("A Beautiful App") ``` Another example would be checking if the user is authenticated when the page loads. If the user is not authenticated, we redirect them to the login page. If they are authenticated, we don't do anything, letting them access the page. This `on_load` event would be placed on every page that requires authentication to access. ```python class State(rx.State): authenticated: bool @rx.event def check_auth(self): # Check if user is authenticated self.authenticated = check_auth() if not self.authenticated: return rx.redirect("/login") @rx.page(on_load=State.check_auth) 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}"), ), ) ``` # Special Events Docs Source: http://localhost:3000/docs/events/special-events.md ```python exec import reflex as rx ``` Reflex also has built-in special events can be found in the [reference](/docs/api-reference/special-events). For example, an event handler can trigger an alert on the browser. ```python demo exec class SpecialEventsState(rx.State): @rx.event def alert(self): return rx.window_alert("Hello World!") def special_events_example(): return rx.button("Alert", on_click=SpecialEventsState.alert) ``` Special events can also be triggered directly in the UI by attaching them to an event trigger. ```python def special_events_example(): return rx.button("Alert", on_click=rx.window_alert("Hello World!")) ``` # Yielding Updates Source: http://localhost:3000/docs/events/yield-events.md ```python exec import reflex as rx ``` A regular event handler will send a `StateUpdate` when it has finished running. This works fine for basic event, but sometimes we need more complex logic. To update the UI multiple times in an event handler, we can `yield` when we want to send an update. To do so, we can use the Python keyword `yield`. For every yield inside the function, a `StateUpdate` will be sent to the frontend with the changes up to this point in the execution of the event handler. This example below shows how to yield 100 updates to the UI. ```python demo exec class MultiUpdateState(rx.State): count: int = 0 @rx.event def timed_update(self): for i in range(100): self.count += 1 yield def multi_update(): return rx.vstack( rx.text(MultiUpdateState.count), rx.button("Start", on_click=MultiUpdateState.timed_update), ) ``` Here is another example of yielding multiple updates with a loading icon. ```python demo exec import asyncio class ProgressExampleState(rx.State): count: int = 0 show_progress: bool = False @rx.event async def increment(self): self.show_progress = True yield # Think really hard. await asyncio.sleep(0.5) self.count += 1 self.show_progress = False def progress_example(): return rx.button( ProgressExampleState.count, on_click=ProgressExampleState.increment, loading=ProgressExampleState.show_progress, ) ``` ```md video https://youtube.com/embed/ITOZkzjtjUA?start=6463&end=6835 # Video: Asyncio with Yield ``` ## Yielding Other Events Events can also yield other events. This is useful when you want to chain events together. To do this, you can yield the event handler function itself. ```md alert # Reference other Event Handler via class When chaining another event handler with `yield`, access it via the state class, not `self`. ``` ```python demo exec import asyncio class YieldEventsState(rx.State): count: int = 0 show_progress: bool = False @rx.event async def add_five(self): self.show_progress = True yield # Think really hard. await asyncio.sleep(1) self.count += 5 self.show_progress = False @rx.event async def increment(self): yield YieldEventsState.add_five yield YieldEventsState.add_five yield YieldEventsState.add_five def multiple_yield_example(): return rx.button( YieldEventsState.count, on_click=YieldEventsState.increment, loading=YieldEventsState.show_progress, ) ``` # Basics Source: http://localhost:3000/docs/getting-started/basics.md ```python exec import reflex as rx ``` **~10 min** · An introduction to the most common concepts you'll use to build Reflex apps. ```md section # You will learn how to: - Create and nest components - Customize and style components - Distinguish between compile-time and runtime - Display data that changes over time - Respond to events and update the screen - Render conditions and lists - Create pages and navigate between them ``` If you haven't yet, [install Reflex](/docs/getting-started/installation) before continuing. Every example below imports the library as `rx`: ```python import reflex as rx ``` ## Creating and nesting components [Components](/docs/ui/overview) are the building blocks for your app's user interface (UI). They are the visual elements that make up your app, like buttons, text, and images. Reflex has a wide selection of [built-in components](/docs/library) to get you started quickly. Components are created using functions that return a component object. ```python demo exec def my_button(): return rx.button("Click Me") ``` Components can be nested inside each other to create complex UIs. To nest components as children, pass them as positional arguments to the parent component. In the example below, the `rx.text` and `my_button` components are children of the `rx.box` component. ```python demo exec def my_container(): return rx.box( rx.text("This is a page"), # Reference components defined in other functions. my_button(), ) ``` You can also use any base HTML element through the [rx.el](/docs/library/html/) namespace. This allows you to use standard HTML elements directly in your Reflex app when you need more control or when a specific component isn't available in the Reflex component library. ```python demo exec def my_div(): return rx.el.div( rx.el.p("Use base html!"), ) ``` If you need a component not provided by Reflex, you can check the [3rd party ecosystem](/docs/custom-components) or [wrap your own React component](/docs/wrapping-react/library-and-tags). ## Customizing and styling components Components can be customized using [props](/docs/components/props), which are passed in as keyword arguments to the component function. Each component has props that are specific to that component. Check the docs for the component you are using to see what props are available. ```python demo exec def half_filled_progress(): return rx.progress(value=50) ``` In addition to component-specific props, components can also be styled using CSS properties passed as props. ```python demo exec def round_button(): return rx.button("Click Me", border_radius="15px", font_size="18px") ``` ```md alert Use the `snake_case` version of the CSS property name as the prop name. ``` See the [styling guide](/docs/styling/overview) for more information on how to style components In summary, components are made up of children and props. ```md definition # Children - Text or other Reflex components nested inside a component. - Passed as **positional arguments**. # Props - Attributes that affect the behavior and appearance of a component. - Passed as **keyword arguments**. ``` ## Displaying data that changes over time Apps need to store and display data that changes over time. Reflex handles this through [State](/docs/state/overview), which is a Python class that stores variables that can change when the app is running, as well as the functions that can change those variables. To define a state class, subclass `rx.State` and define fields that store the state of your app. The state variables ([vars](/docs/vars/base-vars)) should have a type annotation, and can be initialized with a default value. ```python class MyState(rx.State): count: int = 0 ``` ### Referencing state vars in components To reference a state var in a component, you can pass it as a child or prop. The component will automatically update when the state changes. Vars are referenced through class attributes on your state class. For example, to reference the `count` var in a component, use `MyState.count`. ```python demo exec class MyState(rx.State): count: int = 0 color: str = "red" def counter(): return rx.hstack( # The heading `color` prop is set to the `color` var in MyState. rx.heading("Count: ", as_="h2", color=MyState.color), # The `count` var in `MyState` is passed as a child to the heading component. rx.heading(MyState.count, as_="h2"), ) ``` Vars can be referenced in multiple components, and will automatically update when the state changes. ## Responding to events and updating the screen So far, we've defined state vars but we haven't shown how to change them. All state changes are handled through functions in the state class, called [event handlers](/docs/events/events-overview). ```md alert Event handlers are the **only** way to change state in Reflex. ``` Components have special props, such as `on_click`, called event triggers that can be used to make components interactive. Event triggers connect components to event handlers, which update the state. ```python demo exec class CounterState(rx.State): count: int = 0 @rx.event def increment(self): self.count += 1 def counter_increment(): return rx.hstack( rx.heading(CounterState.count, as_="h2"), rx.button("Increment", on_click=CounterState.increment), ) ``` When an event trigger is activated, the event handler is called, which updates the state. The UI is automatically re-rendered to reflect the new state. ```md alert info # What is the `@rx.event` decorator? Adding the `@rx.event` decorator above the event handler is strongly recommended. This decorator enables proper static type checking, which ensures event handlers receive the correct number and types of arguments. This was introduced in Reflex version 0.6.5. ``` ### Event handlers with arguments Event handlers can also take in arguments. For example, the `increment` event handler can take an argument to increment the count by a specific amount. ```python demo exec class CounterState2(rx.State): count: int = 0 @rx.event def increment(self, amount: int): self.count += amount def counter_variable(): return rx.hstack( rx.heading(CounterState2.count, as_="h2"), rx.button("Increment by 1", on_click=lambda: CounterState2.increment(1)), rx.button("Increment by 5", on_click=lambda: CounterState2.increment(5)), ) ``` The `on_click` event trigger doesn't pass any arguments here, but some event triggers do. For example, the `on_blur` event trigger passes the text of an input as an argument to the event handler. ```python demo exec class TextState(rx.State): text: str = "" @rx.event def update_text(self, new_text: str): self.text = new_text def text_input(): return rx.vstack( rx.heading(TextState.text, as_="h2"), rx.input(default_value=TextState.text, on_blur=TextState.update_text), ) ``` ```md alert Make sure that the event handler has the same number of arguments as the event trigger, or an error will be raised. ``` ## Compile-time vs. runtime Before we dive deeper into state, it's important to understand the difference between compile-time and runtime in Reflex. When you run your app, the frontend gets compiled to Javascript code that runs in the browser (compile-time). The backend stays in Python and runs on the server during the lifetime of the app (runtime). ### When can you not use pure Python? We cannot compile arbitrary Python code, only the components that you define. What this means importantly is that you cannot use arbitrary Python operations and functions on state vars in components. However, since any event handlers in your state are on the backend, you **can use any Python code or library** within your state. ### Examples that work Within an event handler, use any Python code or library. ```python demo exec def check_even(num: int): return num % 2 == 0 class MyState3(rx.State): count: int = 0 text: str = "even" @rx.event def increment(self): # Use any Python code within state. # Even reference functions defined outside the state. if check_even(self.count): self.text = "even" else: self.text = "odd" self.count += 1 def count_and_check(): return rx.box( rx.heading(MyState3.text, as_="h2"), rx.button("Increment", on_click=MyState3.increment), ) ``` Use any Python function within components, as long as it is defined at compile time (i.e. does not reference any state var) ```python demo exec def show_numbers(): return rx.vstack(*[rx.hstack(i, check_even(i)) for i in range(10)]) ``` ### Examples that don't work You cannot do an `if` statement on vars in components, since the value is not known at compile time. ```python class BadState(rx.State): count: int = 0 def count_if_even(): return rx.box( rx.heading("Count: ", as_="h2"), # This will raise a compile error, as BadState.count is a var and not known at compile time. rx.text(BadState.count if BadState.count % 2 == 0 else "Odd"), # Using an if statement with a var as a prop will NOT work either. rx.text("hello", color="red" if BadState.count % 2 == 0 else "blue"), ) ``` You cannot do a `for` loop over a list of vars. ```python class BadState(rx.State): items: list[str] = ["Apple", "Banana", "Cherry"] def loop_over_list(): return rx.box( # This will raise a compile error, as BadState.items is a list and not known at compile time. *[rx.text(item) for item in BadState.items] ) ``` You cannot do arbitrary Python operations on state vars in components. ```python class BadTextState(rx.State): text: str = "Hello world" def format_text(): return rx.box( # Python operations such as `len` will not work on state vars. rx.text(len(BadTextState.text)), ) ``` In the next sections, we will show how to handle these cases. ## Conditional rendering As mentioned above, you cannot use Python `if/else` statements with state vars in components. Instead, use the [rx.cond](/docs/components/conditional-rendering) function to conditionally render components. ```python demo exec class LoginState(rx.State): logged_in: bool = False @rx.event def toggle_login(self): self.logged_in = not self.logged_in def show_login(): return rx.box( rx.cond( LoginState.logged_in, rx.heading("Logged In", as_="h2"), rx.heading("Not Logged In", as_="h2"), ), rx.button("Toggle Login", on_click=LoginState.toggle_login), ) ``` ## Rendering lists To iterate over a var that is a list, use the [rx.foreach](/docs/components/rendering-iterables) function to render a list of components. Pass the list var and a function that returns a component as arguments to `rx.foreach`. ```python demo exec class ListState(rx.State): items: list[str] = ["Apple", "Banana", "Cherry"] def render_item(item: rx.Var[str]): """Render a single item.""" # Note that item here is a Var, not a str! return rx.list.item(item) def show_fruits(): return rx.box( rx.foreach(ListState.items, render_item), ) ``` The function that renders each item takes in a `Var`, since this will get compiled up front. ## Var Operations You can't use arbitrary Python operations on state vars in components, but Reflex has [var operations](/docs/vars/var-operations) that you can use to manipulate state vars. For example, to check if a var is even, you can use the `%` and `==` var operations. ```python demo exec class CountEvenState(rx.State): count: int = 0 @rx.event def increment(self): self.count += 1 def count_if_even(): return rx.box( rx.heading("Count: ", as_="h2"), rx.cond( # Here we use the `%` and `==` var operations to check if the count is even. CountEvenState.count % 2 == 0, rx.text("Even"), rx.text("Odd"), ), rx.button("Increment", on_click=CountEvenState.increment), ) ``` ## App and Pages Reflex apps are created by instantiating the `rx.App` class. Pages are linked to specific URL routes, and are created by defining a function that returns a component. ```python def index(): return rx.text("Root Page") app = rx.App() app.add_page(index, route="/") ``` ## Next Steps You've got the core pieces — components, state, events, compile-time vs. runtime. Time to build. ```md alert info # Build something real - [Dashboard tutorial](/docs/getting-started/dashboard-tutorial) — a data app with tables, forms, and state. - [Chatapp tutorial](/docs/getting-started/chatapp-tutorial) — streaming AI responses end-to-end. ``` ```md alert info # Go deeper - [Vars](/docs/vars/base-vars) and [var operations](/docs/vars/var-operations) — the full API. - [Events](/docs/events/events-overview) and [pages](/docs/pages/overview) — routing, triggers, handlers. - [How Reflex works](/docs/advanced-onboarding/how-reflex-works) — what runs where, and why. ``` # AI Chat App Source: http://localhost:3000/docs/getting-started/chatapp-tutorial.md ```python exec from types import SimpleNamespace import reflex as rx # Styling for the live chat demos (the user-facing version lives in the `# style.py` block below). _chat_margin = "15%" _message_style = { "padding": "0.75em 1em", "border_radius": "18px", "margin_y": "0.35em", "max_width": "30em", "display": "inline-block", "line_height": "1.5", "font_size": "0.95rem", } style = SimpleNamespace( question_style=_message_style | { "background_color": rx.color("slate", 3), "color": rx.color("slate", 12), "border": f"1px solid {rx.color('slate', 4)}", "margin_left": _chat_margin, "border_bottom_right_radius": "6px", }, answer_style=_message_style | { "background_color": rx.color("accent", 9), "color": "white", "margin_right": _chat_margin, "border_bottom_left_radius": "6px", }, input_style={"border_width": "1px", "width": "350px"}, button_style={"background_color": rx.color("accent", 9)}, ) ``` **~30 min hands-on** · Build a streaming AI chatbot in pure Python — UI, state, and OpenAI integration in one Reflex app. You can find the full source code for this app on [GitHub](https://github.com/reflex-dev/reflex-chat). ```python eval rx.box( rx.vstack( rx.vstack( rx.box( rx.text("What is Reflex?", style=style.question_style), text_align="right", width="100%", ), rx.box( rx.text( "Reflex is a way to build full-stack web apps in pure Python — " "frontend, backend, and state all in one place.", style=style.answer_style, ), text_align="left", width="100%", ), rx.box( rx.text("Can I deploy it?", style=style.question_style), text_align="right", width="100%", ), rx.box( rx.text( "Yes! A single `reflex deploy` ships it to production.", style=style.answer_style, ), text_align="left", width="100%", ), spacing="0", width="100%", ), rx.hstack( rx.input( placeholder="Ask a question", style=style.input_style, is_disabled=True, ), rx.button("Ask", style=style.button_style, is_disabled=True), spacing="3", padding_top="1.5em", justify="center", width="100%", ), align="stretch", spacing="0", width="100%", padding="2.5em 4em", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", margin_y="1em", ) ``` ## What You'll Learn In this tutorial you'll learn how to: 1. Install `reflex` and set up your development environment. 2. Create components to define and style your UI. 3. Use state to add interactivity to your app. 4. Deploy your app to share with others. ## Setting up Your Project ```md video https://youtube.com/embed/ITOZkzjtjUA?start=175&end=445 # Video: Example of Setting up the Chat App ``` We will start by creating a new project and setting up our development environment. If you haven't installed [uv](https://docs.astral.sh/uv/) yet, see the [installation guide](/docs/getting-started/installation). Then create a new project directory and scaffold a Reflex app: ```bash mkdir chatapp cd chatapp uv init uv add reflex uv run reflex init ``` ```md alert info When prompted to select a template, choose option **0** for a blank project. ``` You can run the template app to make sure everything is working. ```bash uv run reflex run ``` You should see your app running at [http://localhost:3000](http://localhost:3000). Reflex also starts the backend server which handles all the state management and communication with the frontend. You can test the backend server is running by navigating to [http://localhost:8000/ping](http://localhost:8000/ping). Now that we have our project set up, in the next section we will start building our app! ## Basic Frontend Let's start with defining the frontend for our chat app. In Reflex, the frontend can be broken down into independent, reusable components. See the [components docs](/docs/components/props) for more information. ### Display Q&A We will modify the `index` function in `chatapp/chatapp.py` file to return a component that displays a single question and answer. ```python demo box rx.container( rx.box( "What is Reflex?", # The user's question is on the right. text_align="right", ), rx.box( "A way to build web apps in pure Python!", # The answer is on the left. text_align="left", ), ) ``` ```python # chatapp.py import reflex as rx def index() -> rx.Component: return rx.container( rx.box( "What is Reflex?", # The user's question is on the right. text_align="right", ), rx.box( "A way to build web apps in pure Python!", # The answer is on the left. text_align="left", ), ) # Add state and page to the app. app = rx.App() app.add_page(index) ``` Components can be nested inside each other to create complex layouts. Here we create a parent container that contains two boxes for the question and answer. We also add some basic styling to the components. Components take in keyword arguments, called [props](/docs/components/props), that modify the appearance and functionality of the component. We use the `text_align` prop to align the text to the left and right. ### Reusing Components Now that we have a component that displays a single question and answer, we can reuse it to display multiple questions and answers. We will move the component to a separate function `question_answer` and call it from the `index` function. ```python exec def qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(question, text_align="right"), rx.box(answer, text_align="left"), margin_y="1em", ) qa_pairs = [ ("What is Reflex?", "A way to build web apps in pure Python!"), ( "What can I make with it?", "Anything from a simple website to a complex web app!", ), ] def chat() -> rx.Component: qa_pairs = [ ("What is Reflex?", "A way to build web apps in pure Python!"), ( "What can I make with it?", "Anything from a simple website to a complex web app!", ), ] return rx.box(*[qa(question, answer) for question, answer in qa_pairs]) ``` ```python demo box rx.container(chat()) ``` ```python def qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(question, text_align="right"), rx.box(answer, text_align="left"), margin_y="1em", ) def chat() -> rx.Component: qa_pairs = [ ("What is Reflex?", "A way to build web apps in pure Python!"), ( "What can I make with it?", "Anything from a simple website to a complex web app!", ), ] return rx.box(*[qa(question, answer) for question, answer in qa_pairs]) def index() -> rx.Component: return rx.container(chat()) ``` ### Chat Input Now we want a way for the user to input a question. For this, we will use the [input](/docs/library/forms/input) component to have the user add text and a [button](/docs/library/forms/button) component to submit the question. ```python exec def action_bar() -> rx.Component: return rx.hstack( rx.input(placeholder="Ask a question"), rx.button("Ask"), ) ``` ```python demo box rx.container( chat(), action_bar(), ) ``` ```python def action_bar() -> rx.Component: return rx.hstack( rx.input(placeholder="Ask a question"), rx.button("Ask"), ) def index() -> rx.Component: return rx.container( chat(), action_bar(), ) ``` ### Styling Let's add some styling to the app. More information on styling can be found in the [styling docs](/docs/styling/overview). To keep our code clean, we will move the styling to a separate file `chatapp/style.py`. ```python # style.py import reflex as rx # Common styles for questions and answers. shadow = "rgba(0, 0, 0, 0.15) 0px 2px 8px" chat_margin = "20%" message_style = dict( padding="1em", border_radius="5px", margin_y="0.5em", box_shadow=shadow, max_width="30em", display="inline-block", ) # Set specific styles for questions and answers. question_style = message_style | dict( margin_left=chat_margin, background_color=rx.color("gray", 4) ) answer_style = message_style | dict( margin_right=chat_margin, background_color=rx.color("accent", 8) ) # Styles for the action bar. input_style = dict( border_width="1px", padding="0.5em", box_shadow=shadow, width="350px" ) button_style = dict(background_color=rx.color("accent", 10), box_shadow=shadow) ``` We will import the styles in `chatapp.py` and use them in the components. At this point, the app should look like this: ```python exec def styled_qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(rx.text(question, style=style.question_style), text_align="right"), rx.box(rx.text(answer, style=style.answer_style), text_align="left"), margin_y="1em", width="100%", ) def styled_chat() -> rx.Component: qa_pairs = [ ("What is Reflex?", "A way to build web apps in pure Python!"), ( "What can I make with it?", "Anything from a simple website to a complex web app!", ), ] return rx.box(*[styled_qa(question, answer) for question, answer in qa_pairs]) def styled_action_bar() -> rx.Component: return rx.hstack( rx.input(placeholder="Ask a question", style=style.input_style), rx.button("Ask", style=style.button_style), ) ``` ```python demo box rx.center( rx.vstack( styled_chat(), styled_action_bar(), align="center", ) ) ``` ```python # chatapp.py import reflex as rx from chatapp import style def qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(rx.text(question, style=style.question_style), text_align="right"), rx.box(rx.text(answer, style=style.answer_style), text_align="left"), margin_y="1em", width="100%", ) def chat() -> rx.Component: qa_pairs = [ ("What is Reflex?", "A way to build web apps in pure Python!"), ( "What can I make with it?", "Anything from a simple website to a complex web app!", ), ] return rx.box(*[qa(question, answer) for question, answer in qa_pairs]) def action_bar() -> rx.Component: return rx.hstack( rx.input(placeholder="Ask a question", style=style.input_style), rx.button("Ask", style=style.button_style), ) def index() -> rx.Component: return rx.center( rx.vstack( chat(), action_bar(), align="center", ) ) app = rx.App() app.add_page(index) ``` The app is looking good, but it's not very useful yet! In the next section, we will add some functionality to the app. ## State Now let’s make the chat app interactive by adding state. The state is where we define all the variables that can change in the app and all the functions that can modify them. You can learn more about state in the [state docs](/docs/state/overview). ### Defining State We will create a new file called `state.py` in the `chatapp` directory. Our state will keep track of the current question being asked and the chat history. We will also define an event handler `answer` which will process the current question and add the answer to the chat history. ```python # state.py import reflex as rx class State(rx.State): # The current question being asked. question: str # Keep track of the chat history as a list of (question, answer) tuples. chat_history: list[tuple[str, str]] @rx.event def set_question(self, value: str): self.question = value @rx.event def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, answer)) ``` ### Binding State to Components Now we can import the state in `chatapp.py` and reference it in our frontend components. We will modify the `chat` component to use the state instead of the current fixed questions and answers. ```python exec class BasicChatState(rx.State): question: str = "" chat_history: list[tuple[str, str]] = [] @rx.event def set_question(self, value: str): self.question = value @rx.event def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, answer)) def qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(rx.text(question, style=style.question_style), text_align="right"), rx.box(rx.text(answer, style=style.answer_style), text_align="left"), margin_y="1em", width="100%", ) def basic_chat() -> rx.Component: return rx.box( rx.foreach( BasicChatState.chat_history, lambda messages: qa(messages[0], messages[1]) ) ) def basic_action_bar() -> rx.Component: return rx.hstack( rx.input( placeholder="Ask a question", on_change=BasicChatState.set_question, style=style.input_style, ), rx.button("Ask", on_click=BasicChatState.answer, style=style.button_style), ) ``` ```python demo box rx.container( basic_chat(), basic_action_bar(), ) ``` ```python # chatapp.py from chatapp.state import State def chat() -> rx.Component: return rx.box( rx.foreach(State.chat_history, lambda messages: qa(messages[0], messages[1])) ) def action_bar() -> rx.Component: return rx.hstack( rx.input( placeholder="Ask a question", on_change=State.set_question, style=style.input_style, ), rx.button("Ask", on_click=State.answer, style=style.button_style), ) ``` Normal Python `for` loops don't work for iterating over state vars because these values can change and aren't known at compile time. Instead, we use the [foreach](/docs/library/dynamic-rendering/foreach) component to iterate over the chat history. We also bind the input's `on_change` event to the `set_question` event handler, which will update the `question` state var while the user types in the input. We bind the button's `on_click` event to the `answer` event handler, which will process the question and add the answer to the chat history. ### Clearing the Input Currently the input doesn't clear after the user clicks the button. We can fix this by binding the value of the input to `question`, with `value=State.question`, and clear it when we run the event handler for `answer`, with `self.question = ''`. ```python exec class ClearingChatState(rx.State): question: str = "" chat_history: list[tuple[str, str]] = [] @rx.event def set_question(self, value: str): self.question = value @rx.event def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, answer)) # Clear the question input. self.question = "" def clearing_chat() -> rx.Component: return rx.box( rx.foreach( ClearingChatState.chat_history, lambda messages: qa(messages[0], messages[1]), ) ) def clearing_action_bar() -> rx.Component: return rx.hstack( rx.input( value=ClearingChatState.question, placeholder="Ask a question", on_change=ClearingChatState.set_question, style=style.input_style, ), rx.button("Ask", on_click=ClearingChatState.answer, style=style.button_style), ) ``` ```python demo box rx.container( clearing_chat(), clearing_action_bar(), ) ``` ```python # chatapp.py def action_bar() -> rx.Component: return rx.hstack( rx.input( value=State.question, placeholder="Ask a question", on_change=State.set_question, style=style.input_style, ), rx.button("Ask", on_click=State.answer, style=style.button_style), ) ``` ```python # state.py @rx.event def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, answer)) self.question = "" ``` ### Streaming Text Normally state updates are sent to the frontend when an event handler returns. However, we want to stream the text from the chatbot as it is generated. We can do this by yielding from the event handler. See the [yield events docs](/docs/events/yield-events) for more info. ```python exec import asyncio class StreamingChatState(rx.State): question: str = "" chat_history: list[tuple[str, str]] = [] @rx.event def set_question(self, value: str): self.question = value @rx.event async def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, "")) # Clear the question input. self.question = "" # Yield here to clear the frontend input before continuing. yield for i in range(len(answer)): await asyncio.sleep(0.1) self.chat_history[-1] = (self.chat_history[-1][0], answer[: i + 1]) yield def streaming_chat() -> rx.Component: return rx.box( rx.foreach( StreamingChatState.chat_history, lambda messages: qa(messages[0], messages[1]), ) ) def streaming_action_bar() -> rx.Component: return rx.hstack( rx.input( value=StreamingChatState.question, placeholder="Ask a question", on_change=StreamingChatState.set_question, style=style.input_style, ), rx.button("Ask", on_click=StreamingChatState.answer, style=style.button_style), ) ``` ```python demo box rx.container( streaming_chat(), streaming_action_bar(), ) ``` ```python # state.py import asyncio async def answer(self): # Our chatbot is not very smart right now... answer = "I don't know!" self.chat_history.append((self.question, "")) # Clear the question input. self.question = "" # Yield here to clear the frontend input before continuing. yield for i in range(len(answer)): # Pause to show the streaming effect. await asyncio.sleep(0.1) # Add one letter at a time to the output. self.chat_history[-1] = (self.chat_history[-1][0], answer[: i + 1]) yield ``` In the next section, we will finish our chatbot by adding AI! ## Final App We will use OpenAI's API to give our chatbot some intelligence. ### Configure OpenAI First, ensure you have an active OpenAI subscription and install the latest `openai` package: ```bash pip install --upgrade openai ``` Then export your API key so the app can read it at runtime: ```bash export OPENAI_API_KEY="sk-..." ``` ### Using the API Making your chatbot intelligent requires connecting to a language model API. This section explains how to integrate with OpenAI's API to power your chatbot's responses. 1. First, the user types a prompt that is updated via the `on_change` event handler. 2. Next, when a prompt is ready, the user can choose to submit it by clicking the `Ask` button which in turn triggers the `State.answer` method inside our `state.py` file. 3. Finally, if the method is triggered, the `prompt` is sent via a request to OpenAI client and returns an answer that we can trim and use to update the chat history! ```python # chatapp.py def action_bar() -> rx.Component: return rx.hstack( rx.input( value=State.question, placeholder="Ask a question", # on_change event updates the input as the user types a prompt. on_change=State.set_question, style=style.input_style, ), # on_click event triggers the API to send the prompt to OpenAI. rx.button("Ask", on_click=State.answer, style=style.button_style), ) ``` ```python # state.py import os from openai import AsyncOpenAI @rx.event async def answer(self): # Our chatbot has some brains now! client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"]) session = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": self.question}], stop=None, temperature=0.7, stream=True, ) # Add to the answer as the chatbot responds. answer = "" self.chat_history.append((self.question, answer)) # Clear the question input. self.question = "" # Yield here to clear the frontend input before continuing. yield async for item in session: if hasattr(item.choices[0].delta, "content"): if item.choices[0].delta.content is None: # presence of 'None' indicates the end of the response break answer += item.choices[0].delta.content self.chat_history[-1] = (self.chat_history[-1][0], answer) yield ``` Finally, we have our chatbot! ### Final Code The finished project is split across three files — `chatapp.py` for UI and app setup, `state.py` for state and API integration, and `style.py` for styling: ```text chatapp/ ├── chatapp.py ├── state.py └── style.py ``` The `chatapp.py` file: ```python import reflex as rx from chatapp import style from chatapp.state import State def qa(question: str, answer: str) -> rx.Component: return rx.box( rx.box(rx.text(question, style=style.question_style), text_align="right"), rx.box(rx.text(answer, style=style.answer_style), text_align="left"), margin_y="1em", ) def chat() -> rx.Component: return rx.box( rx.foreach( State.chat_history, lambda messages: qa(messages[0], messages[1]), ) ) def action_bar() -> rx.Component: return rx.hstack( rx.input( value=State.question, placeholder="Ask a question", on_change=State.set_question, style=style.input_style, ), rx.button( "Ask", on_click=State.answer, style=style.button_style, ), ) def index() -> rx.Component: return rx.center( rx.vstack( chat(), action_bar(), align="center", ) ) app = rx.App() app.add_page(index) ``` The `state.py` file: ```python import os from openai import AsyncOpenAI import reflex as rx class State(rx.State): question: str chat_history: list[tuple[str, str]] = [] @rx.event def set_question(self, value: str): self.question = value async def answer(self): client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"]) # Start streaming completion from OpenAI session = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": self.question}], temperature=0.7, stream=True, ) # Initialize response and update UI answer = "" self.chat_history.append((self.question, answer)) self.question = "" yield # Process streaming response async for item in session: if hasattr(item.choices[0].delta, "content"): if item.choices[0].delta.content is None: break answer += item.choices[0].delta.content self.chat_history[-1] = (self.chat_history[-1][0], answer) yield ``` The `style.py` file: ```python import reflex as rx # Common style base shadow = "rgba(0, 0, 0, 0.15) 0px 2px 8px" chat_margin = "20%" message_style = dict( padding="1em", border_radius="5px", margin_y="0.5em", box_shadow=shadow, max_width="30em", display="inline-block", ) # Styles for questions and answers question_style = message_style | dict( margin_left=chat_margin, background_color=rx.color("gray", 4), ) answer_style = message_style | dict( margin_right=chat_margin, background_color=rx.color("accent", 8), ) # Styles for input elements input_style = dict( border_width="1px", padding="0.5em", box_shadow=shadow, width="350px" ) button_style = dict(background_color=rx.color("accent", 10), box_shadow=shadow) ``` ### Next Steps Congratulations! You have built your first chatbot. From here, you can read through the rest of the documentations to learn about Reflex in more detail. The best way to learn is to build something, so try to build your own app using this as a starting point! ### One More Thing With our hosting service, you can deploy this app with a single command within minutes. Check out our [Hosting Quick Start](https://reflex.dev/docs/hosting/deploy-quick-start/). # Data Dashboard Source: http://localhost:3000/docs/getting-started/dashboard-tutorial.md ```python exec import reflex as rx ``` **~20 min hands-on** · Build a small data dashboard where users can input data that renders in a table and a graph. This tutorial does not assume any existing Reflex knowledge, but we do recommend checking out the quick [Basics Guide](/docs/getting-started/basics) first. The techniques you'll learn are fundamental to any Reflex app. This tutorial is divided into several sections: - **Setup**: Get your machine ready. - **Overview**: Components and props. - **Dynamic data with State**: Render data that changes. - **Add data with a form**: Forms + event handlers. - **Plot a graph**: Reflex's graphing components. - **Customize** + **[Full app](#full-app-styled)**: Customize and see the finished code. ## What are you building? An interactive data dashboard: a table of users, a form to add more, and a bar chart that updates as data changes. Want to skip ahead? Jump to the [Full app](#full-app-styled) at the bottom. ```python exec import dataclasses from collections import Counter @dataclasses.dataclass class User: """The user model.""" name: str email: str gender: str class State5(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"), ] users_for_graph: list[dict] = [] def add_user(self, form_data: dict): self.users.append(User(**form_data)) self.transform_data() return rx.toast.info( f"User {form_data['name']} has been added.", position="bottom-right", ) def transform_data(self): """Transform user gender group data into a format suitable for visualization in graphs.""" # Count users of each gender group gender_counts = Counter(user.gender for user in self.users) # Transform into list of dict so it can be used in the graph self.users_for_graph = [ {"name": gender_group, "value": count} for gender_group, count in gender_counts.items() ] def show_user5(user: User): """Show a user in a table row.""" 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", ) def add_customer_button5() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State5.add_user, reset_on_submit=False, ), max_width="450px", ), ) def graph5(): return rx.recharts.bar_chart( rx.recharts.bar( data_key="value", fill=rx.color("accent", 9), radius=6, bar_size=48, ), rx.recharts.x_axis( data_key="name", tick_line=False, axis_line=False, padding={"left": 24, "right": 24}, ), rx.recharts.y_axis( tick_line=False, axis_line=False, allow_decimals=False, ), rx.recharts.cartesian_grid( stroke_dasharray="3 3", vertical=False, stroke=rx.color("slate", 4), ), data=State5.users_for_graph, width="100%", height=200, margin={"top": 8, "right": 8, "bottom": 0, "left": 0}, ) ``` ```python eval rx.box( rx.vstack( rx.hstack( rx.vstack( rx.text( "Users", size="4", weight="bold", color=rx.color("slate", 12), text_align="left", width="100%", ), rx.text( "Add customers and watch the chart update.", size="2", color=rx.color("slate", 10), text_align="left", width="100%", ), spacing="1", align="start", ), rx.spacer(), add_customer_button5(), align="center", width="100%", ), 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(State5.users, show_user5), ), variant="surface", size="2", width="100%", ), graph5(), align="stretch", width="100%", on_mouse_enter=State5.transform_data, spacing="4", padding="1.75em 2em", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", margin_y="1em", background=rx.color("slate", 1), ) ``` ## Setup 1. [Install Reflex](/docs/getting-started/installation) if you haven't already. 2. Create a folder called `dashboard_tutorial` and `cd` into it. 3. Run `uv init` and `uv add reflex`. 4. Run `uv run reflex init` and choose template `0` (the blank template). 5. Run `uv run reflex run` to start the app and confirm everything works. ## Overview ### Starter code The `reflex init` command scaffolds an `rxconfig.py` (app [config](/docs/advanced-onboarding/configuration)), an `assets/` folder for static files, and a `dashboard_tutorial/dashboard_tutorial.py` module containing your app. Open that module and replace its contents — we'll build the app up from scratch. A minimal Reflex page is just a component function plus an app that registers it: ```python import reflex as rx def index() -> rx.Component: return rx.text("Hello World!") app = rx.App() app.add_page(index) ``` ```md alert info For the rest of the tutorial the `app = rx.App()` and `app.add_page` lines are implied and not shown — we'll come back to them in [Customize](#customize). ``` ### Create a table The `rx.table` component has a `root` that wraps a `header` and a `body`. The header takes `row` → `column_header_cell` components; the body takes `row` → `cell` components holding the actual data. Props like `variant` and `size` customize the look: ```python eval 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.table.row( rx.table.cell("Danilo Sousa"), rx.table.cell("danilo@example.com"), rx.table.cell("Male"), ), rx.table.row( rx.table.cell("Zahra Ambessa"), rx.table.cell("zahra@example.com"), rx.table.cell("Female"), ), ), variant="surface", size="3", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` ```python def index() -> rx.Component: return 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.table.row( rx.table.cell("Danilo Sousa"), rx.table.cell("danilo@example.com"), rx.table.cell("Male"), ), rx.table.row( rx.table.cell("Zahra Ambessa"), rx.table.cell("zahra@example.com"), rx.table.cell("Female"), ), ), variant="surface", size="3", ) ``` ## Dynamic data with State The table above is static — the rows are hardcoded. To make it dynamic, we move the data onto **state**: a Python class whose fields ([state vars](/docs/state/overview)) hold the app's data and whose methods ([event handlers](/docs/events/events-overview)) mutate them. We'll model each row as a `User` dataclass so we can access fields by name (`user.name`) instead of by index: ```python import dataclasses @dataclasses.dataclass class User: name: str email: str gender: str 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"), ] ``` To iterate a list state var, use [`rx.foreach`](/docs/components/rendering-iterables) — it takes an iterable and a function that renders each item. Here `show_user` receives a `User` and returns a `table.row`: ```python def show_user(user: User) -> rx.Component: return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) def index() -> rx.Component: return 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="surface", size="3", ) ``` ```md alert info # Why not a `for` loop? A regular `for` loop runs at compile time, but state vars change at runtime — so the rendered rows wouldn't update. `rx.foreach` tells the compiler to re-render when the state var changes. See [compile-time vs runtime](/docs/getting-started/basics#compile-time-vs.-runtime). ``` ```python exec import dataclasses @dataclasses.dataclass class User: """The user model.""" name: str email: str gender: str class State2(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"), ] def show_user2(user: User): """Show a person in a table row.""" return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) ``` ```python eval 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(State2.users, show_user2), ), variant="surface", size="3", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` The table looks the same, but the rows now come from state — next we'll add a form that appends to `State.users` so new rows appear automatically. ## Add data with a form We build a form using `rx.form`, which takes several components such as `rx.input` and `rx.select`, which represent the form fields that allow you to add information to submit with the form. Check out the [form](/docs/library/forms/form) docs for more information on form components. The `rx.input` component takes in several props. The `placeholder` prop is the text that is displayed in the input field when it is empty. The `name` prop is the name of the input field, which gets passed through in the dictionary when the form is submitted. The `required` prop is a boolean that determines if the input field is required. The `rx.select` component takes in a list of options that are displayed in the dropdown. The other props used here are identical to the `rx.input` component. ```python demo rx.form( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), ) ``` This form is all very compact as you can see from the example, so we need to add some styling to make it look better. We can do this by adding a `vstack` component around the form fields. The `vstack` component stacks the form fields vertically. Check out the [layout](/docs/styling/layout) docs for more information on how to layout your app. ```python demo rx.form( rx.vstack( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), ), ) ``` Now you have probably realised that we have all the form fields, but we have no way to submit the form. We can add a submit button to the form by adding a `rx.button` component to the `vstack` component. The `rx.button` component takes in the text that is displayed on the button and the `type` prop which is the type of button. The `type` prop is set to `submit` so that the form is submitted when the button is clicked. In addition to this we need a way to update the `users` state variable when the form is submitted. All state changes are handled through functions in the state class, called [event handlers](/docs/events/events-overview). Components have special props called event triggers, such as `on_submit`, that can be used to make components interactive. Event triggers connect components to event handlers, which update the state. Different event triggers expect the event handler that you hook them up to, to take in different arguments (and some do not take in any arguments). The `on_submit` event trigger of `rx.form` is hooked up to the `add_user` event handler that is defined in the `State` class. This event trigger expects to pass a `dict`, containing the form data, to the event handler that it is hooked up to. The `add_user` event handler takes in the form data as a dictionary and appends it to the `users` state variable. ```python class State(rx.State): ... def add_user(self, form_data: dict): self.users.append(User(**form_data)) def form(): return rx.form( rx.vstack( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.button("Submit", type="submit"), ), on_submit=State.add_user, reset_on_submit=True, ) ``` Finally we must add the new `form()` component we have defined to the `index()` function so that the form is rendered on the page. Below is the full code for the app so far. If you try this form out you will see that you can add new users to the table by filling out the form and clicking the submit button. The form data will also appear as a toast (a small window in the corner of the page) on the screen when submitted. ```python exec class State3(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"), ] def add_user(self, form_data: dict): self.users.append(User(**form_data)) return rx.toast.info( f"User has been added: {form_data}.", position="bottom-right", ) def show_user(user: User): """Show a person in a table row.""" return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) def form(): return rx.form( rx.vstack( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.button("Submit", type="submit"), ), on_submit=State3.add_user, reset_on_submit=True, ) ``` ```python eval rx.vstack( form(), 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(State3.users, show_user), ), variant="surface", size="3", ), spacing="4", border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` ```python 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"), ] def add_user(self, form_data: dict): self.users.append(User(**form_data)) def show_user(user: User): """Show a person in a table row.""" return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) def form(): return rx.form( rx.vstack( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.button("Submit", type="submit"), ), on_submit=State.add_user, reset_on_submit=True, ) def index() -> rx.Component: return rx.vstack( form(), 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="surface", size="3", ), ) ``` ### Put the form in a dialog In Reflex, we like to make the user interaction as intuitive as possible. Placing the form we just constructed in an overlay creates a focused interaction by dimming the background, and ensures a cleaner layout when you have multiple action points such as editing and deleting as well. We will place the form inside of a `rx.dialog` component (also called a modal). The `rx.dialog.root` contains all the parts of a dialog, and the `rx.dialog.trigger` wraps the control that will open the dialog. In our case the trigger will be an `rx.button` that says "Add User" as shown below. ```python rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ) ``` After the trigger we have the `rx.dialog.content` which contains everything within our dialog, including a title, a description and our form. The first way to close the dialog is without submitting the form and the second way is to close the dialog by submitting the form as shown below. This requires two `rx.dialog.close` components within the dialog. ```python ( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), ) rx.dialog.close( rx.button("Submit", type="submit"), ) ``` The total code for the dialog with the form in it is below. ```python demo rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( # flex is similar to vstack and used to layout the form fields rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State3.add_user, reset_on_submit=False, ), # max_width is used to limit the width of the dialog max_width="450px", ), ) ``` At this point we have an app that allows you to add users to a table by filling out a form. The form is placed in a dialog that can be opened by clicking the "Add User" button. We change the name of the component from `form` to `add_customer_button` and update this in our `index` component. The full app so far and code are below. ```python exec def add_customer_button() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State3.add_user, reset_on_submit=False, ), max_width="450px", ), ) ``` ```python eval rx.vstack( add_customer_button(), 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(State3.users, show_user), ), variant="surface", size="3", ), spacing="4", border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` ```python @dataclasses.dataclass class User: """The user model.""" name: str email: str gender: str 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"), ] def add_user(self, form_data: dict): self.users.append(User(**form_data)) def show_user(user: User): """Show a person in a table row.""" return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) def add_customer_button() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State.add_user, reset_on_submit=False, ), max_width="450px", ), ) def index() -> rx.Component: return rx.vstack( add_customer_button(), 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="surface", size="3", ), ) ``` ## Plot a graph Next we'll plot the user data in a graph using Reflex's built-in recharts library, counting users by gender. ### Transform the data The graphing components in Reflex expect to take in a list of dictionaries. Each dictionary represents a data point on the graph and contains the x and y values. We will create a new event handler in the state called `transform_data` to transform the user data into the format that the graphing components expect. We must also create a new state variable called `users_for_graph` to store the transformed data, which will be used to render the graph. ```python from collections import Counter class State(rx.State): users: list[User] = [] users_for_graph: list[dict] = [] def add_user(self, form_data: dict): self.users.append(User(**form_data)) self.transform_data() def transform_data(self): """Transform user gender group data into a format suitable for visualization in graphs.""" # Count users of each gender group gender_counts = Counter(user.gender for user in self.users) # Transform into list of dict so it can be used in the graph self.users_for_graph = [ {"name": gender_group, "value": count} for gender_group, count in gender_counts.items() ] ``` As we can see above the `transform_data` event handler uses the `Counter` class from the `collections` module to count the number of users of each gender. We then create a list of dictionaries from this which we set to the state var `users_for_graph`. Finally we can see that whenever we add a new user through submitting the form and running the `add_user` event handler, we call the `transform_data` event handler to update the `users_for_graph` state variable. ### Render the graph We use the `rx.recharts.bar_chart` component to render the graph. We pass through the state variable for our graphing data as `data=State.users_for_graph`. We also pass in a `rx.recharts.bar` component which represents the bars on the graph. The `rx.recharts.bar` component takes in the `data_key` prop which is the key in the data dictionary that represents the y value of the bar. The `stroke` and `fill` props are used to set the color of the bars. The `rx.recharts.bar_chart` component also takes in `rx.recharts.x_axis` and `rx.recharts.y_axis` components which represent the x and y axes of the graph. The `data_key` prop of the `rx.recharts.x_axis` component is set to the key in the data dictionary that represents the x value of the bar. Finally we add `width` and `height` props to set the size of the graph. ```python def graph(): return rx.recharts.bar_chart( rx.recharts.bar( data_key="value", stroke=rx.color("accent", 9), fill=rx.color("accent", 8), ), rx.recharts.x_axis(data_key="name"), rx.recharts.y_axis(), data=State.users_for_graph, width="100%", height=250, ) ``` Finally we add this `graph()` component to our `index()` component so that the graph is rendered on the page. The code for the full app with the graph included is below. If you try this out you will see that the graph updates whenever you add a new user to the table. ```python exec from collections import Counter class State4(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"), ] users_for_graph: list[dict] = [] def add_user(self, form_data: dict): self.users.append(User(**form_data)) self.transform_data() return rx.toast.info( f"User {form_data['name']} has been added.", position="bottom-right", ) def transform_data(self): """Transform user gender group data into a format suitable for visualization in graphs.""" # Count users of each gender group gender_counts = Counter(user.gender for user in self.users) # Transform into list of dict so it can be used in the graph self.users_for_graph = [ {"name": gender_group, "value": count} for gender_group, count in gender_counts.items() ] def add_customer_button() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="Male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State4.add_user, reset_on_submit=False, ), max_width="450px", ), ) def graph(): return rx.recharts.bar_chart( rx.recharts.bar( data_key="value", stroke=rx.color("accent", 9), fill=rx.color("accent", 8), ), rx.recharts.x_axis(data_key="name"), rx.recharts.y_axis(), data=State4.users_for_graph, width="100%", height=250, ) ``` ```python eval rx.vstack( add_customer_button(), 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(State4.users, show_user), ), variant="surface", size="3", ), graph(), spacing="4", border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` ```python from collections import Counter 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"), ] users_for_graph: list[dict] = [] def add_user(self, form_data: dict): self.users.append(User(**form_data)) self.transform_data() def transform_data(self): """Transform user gender group data into a format suitable for visualization in graphs.""" # Count users of each gender group gender_counts = Counter(user.gender for user in self.users) # Transform into list of dict so it can be used in the graph self.users_for_graph = [ {"name": gender_group, "value": count} for gender_group, count in gender_counts.items() ] def show_user(user: User): """Show a person in a table row.""" return rx.table.row( rx.table.cell(user.name), rx.table.cell(user.email), rx.table.cell(user.gender), ) def add_customer_button() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State.add_user, reset_on_submit=False, ), max_width="450px", ), ) def graph(): return rx.recharts.bar_chart( rx.recharts.bar( data_key="value", stroke=rx.color("accent", 9), fill=rx.color("accent", 8), ), rx.recharts.x_axis(data_key="name"), rx.recharts.y_axis(), data=State.users_for_graph, width="100%", height=250, ) def index() -> rx.Component: return rx.vstack( add_customer_button(), 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="surface", size="3", ), graph(), ) ``` If you run the app locally with no seed users, the graph is empty until you add one — `transform_data` only runs when a user is added. The next section fixes that by calling it on page load. ## Customize ### Revisit `app.add_page` At the beginning of this tutorial we mentioned that the `app.add_page` function is required for every Reflex app. This function is used to add a component to a page. The `app.add_page` currently looks like this `app.add_page(index)`. We could change the route that the page renders on by setting the `route` prop such as `route="/custom-route"`, this would change the route to `http://localhost:3000/custom-route` for this page. We can also set a `title` to be shown in the browser tab and a `description` as shown in search results. To solve the problem we had above about our graph not loading when the page loads, we can use `on_load` inside of `app.add_page` to call the `transform_data` event handler when the page loads. This would look like `on_load=State.transform_data`. Below see what our `app.add_page` would look like with some of the changes above added. ```python eval rx.vstack( add_customer_button(), 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(State4.users, show_user), ), variant="surface", size="3", ), graph(), on_mouse_enter=State4.transform_data, spacing="4", border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", padding="2em", ) ``` ```python app.add_page( index, title="Customer Data App", description="A simple app to manage customer data.", on_load=State.transform_data, ) ``` ### Revisit `rx.App()` At the beginning of the tutorial we also mentioned that we defined our app using `app=rx.App()`. We can also pass in some props to the `rx.App` component to customize the app. The most important one is `theme` which allows you to customize the look and feel of the app. The `theme` prop takes in an `rx.theme` component which has several props that can be set. The `radius` prop sets the global radius value for the app that is inherited by all components that have a `radius` prop. It can be overwritten locally for a specific component by manually setting the `radius` prop. The `accent_color` prop sets the accent color of the app. See the [theme docs](/docs/library/other/theme) for the full list of options. To see other props that can be set at the app level check out this [documentation](/docs/styling/theming) ```python app = rx.App( theme=rx.theme(radius="full", accent_color="grass"), ) ``` The theme applies at the app level, so you'll need to run locally to see it in action. ## Full app styled Finally let's make some styling updates. We will add hover styling to the table rows and center the table inside `show_user` with `style={"_hover": {"bg": rx.color("gray", 3)}}, align="center"`. In addition, we will add some `width="100%"` and `align="center"` to the `index()` component to center the items on the page and ensure they stretch the full width of the page. Check out the full code and interactive app below: ```python eval rx.box( rx.vstack( rx.hstack( rx.vstack( rx.text( "Users", size="4", weight="bold", color=rx.color("slate", 12), text_align="left", width="100%", ), rx.text( "Add customers and watch the chart update.", size="2", color=rx.color("slate", 10), text_align="left", width="100%", ), spacing="1", align="start", ), rx.spacer(), add_customer_button5(), align="center", width="100%", ), 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(State5.users, show_user5), ), variant="surface", size="2", width="100%", ), graph5(), align="stretch", width="100%", on_mouse_enter=State5.transform_data, spacing="4", padding="1.75em 2em", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", margin_y="1em", background=rx.color("slate", 1), ) ``` ```python import reflex as rx from collections import Counter @dataclasses.dataclass class User: """The user model.""" name: str email: str gender: str 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"), ] users_for_graph: list[dict] = [] def add_user(self, form_data: dict): self.users.append(User(**form_data)) self.transform_data() def transform_data(self): """Transform user gender group data into a format suitable for visualization in graphs.""" # Count users of each gender group gender_counts = Counter(user.gender for user in self.users) # Transform into list of dict so it can be used in the graph self.users_for_graph = [ {"name": gender_group, "value": count} for gender_group, count in gender_counts.items() ] def show_user(user: User): """Show a user in a table row.""" 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", ) def add_customer_button() -> rx.Component: return rx.dialog.root( rx.dialog.trigger( rx.button( rx.icon("plus", size=26), rx.text("Add User", size="4"), ), ), rx.dialog.content( rx.dialog.title( "Add New User", ), rx.dialog.description( "Fill the form with the user's info", ), rx.form( rx.flex( rx.input(placeholder="User Name", name="name", required=True), rx.input( placeholder="user@reflex.dev", name="email", ), rx.select( ["Male", "Female"], placeholder="male", name="gender", ), rx.flex( rx.dialog.close( rx.button( "Cancel", variant="soft", color_scheme="gray", ), ), rx.dialog.close( rx.button("Submit", type="submit"), ), spacing="3", justify="end", ), direction="column", spacing="4", ), on_submit=State.add_user, reset_on_submit=False, ), max_width="450px", ), ) def graph(): return rx.recharts.bar_chart( rx.recharts.bar( data_key="value", fill=rx.color("accent", 9), radius=6, bar_size=48, ), rx.recharts.x_axis( data_key="name", tick_line=False, axis_line=False, padding={"left": 24, "right": 24}, ), rx.recharts.y_axis( tick_line=False, axis_line=False, allow_decimals=False, ), rx.recharts.cartesian_grid( stroke_dasharray="3 3", vertical=False, stroke=rx.color("slate", 4), ), data=State.users_for_graph, width="100%", height=200, margin={"top": 8, "right": 8, "bottom": 0, "left": 0}, ) def index() -> rx.Component: return rx.box( rx.vstack( rx.hstack( rx.vstack( rx.text( "Users", size="4", weight="bold", color=rx.color("slate", 12), text_align="left", width="100%", ), rx.text( "Add customers and watch the chart update.", size="2", color=rx.color("slate", 10), text_align="left", width="100%", ), spacing="1", align="start", ), rx.spacer(), add_customer_button(), align="center", width="100%", ), 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="surface", size="2", width="100%", ), graph(), align="stretch", width="100%", spacing="4", padding="1.75em 2em", ), border=f"1px solid {rx.color('slate', 5)}", border_radius="12px", margin_y="1em", background=rx.color("slate", 1), ) app = rx.App( theme=rx.theme(radius="full", accent_color="grass"), ) app.add_page( index, title="Customer Data App", description="A simple app to manage customer data.", on_load=State.transform_data, ) ``` ## Recap You built: - A table that displays user data. - A form (inside a dialog) to add new users. - A bar chart that visualizes the distribution. Along the way you learned: - **State** — how to store data that changes over time. - **Events** — how to respond to user actions and update the UI. - **Styling** — tweaking theme, layout, and hover states. # Installation Source: http://localhost:3000/docs/getting-started/installation.md ~3 minutes · Requires Python 3.10+. ```md alert info # Reading this as an AI agent or coding assistant? You're likely here to build a Reflex app for someone. Reflex does **not** behave like the Flask/FastAPI/React code in your training data — read this before writing any code. **Get the current docs (don't rely on training data — the API changes between versions):** - Fetch [`https://reflex.dev/docs/ai/integrations/ai-onboarding.md`](https://reflex.dev/docs/ai/integrations/ai-onboarding.md) — the AI onboarding guide: every way to get current docs (Markdown pages, llms.txt, MCP, skills) plus quick-start prompts. - Fetch [`https://reflex.dev/llms.txt`](https://reflex.dev/llms.txt) for a token-efficient map of the docs and core concepts. - Use the [Reflex MCP server](/docs/ai/integrations/mcp-overview/) (`https://build.reflex.dev/mcp`) for live component/prop lookup so you don't hallucinate APIs. *(MCP access is an enterprise feature.)* - Run `uv run reflex --version` and trust the live docs for that version over memory. **Drop a rules file in the project root** (`AGENTS.md`, or `CLAUDE.md`/`.cursorrules` for your tool) so these conventions persist across your session — `reflex init` writes a starter `AGENTS.md` for you by default, and a minimal fallback is below. ``` Starter rules file: ```text # Reflex conventions - Reflex is pure Python that compiles to a React frontend. Do NOT write JS, HTML, or JSX. - Components are function calls that return components; pass props as keyword args. - NEVER use plain Python control flow on state Vars inside the render tree. No `if`, `for`, `len()`, or f-strings over a Var — use `rx.cond`, `rx.foreach`, and Var operators instead. (This is the most common mistake.) - State lives in `rx.State` subclasses. State only mutates inside event-handler methods — never at module load or render time. Derived values use `@rx.var`. - Event handlers may be `async` and may `yield` to push intermediate UI updates. - Always run commands with `uv run` (e.g. `uv run reflex run`). Never bare `python`. - `.web/` is generated output — never edit or commit it. `rxconfig.py` is the config entry point. - Verify your work headlessly: `CI=1 uv run reflex run` (frontend :3000, backend :8000), add `--loglevel debug` to diagnose failures. ``` ## Virtual Environment We recommend [uv](https://docs.astral.sh/uv/) as the default; [venv](https://docs.python.org/3/library/venv.html), [conda](https://conda.io/), and [poetry](https://python-poetry.org/) are alternatives. ## Install Reflex on your system `````md tabs ## macOS/Linux ### Install uv ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` After installation, restart your terminal or run `source ~/.bashrc` (or `source ~/.zshrc` for zsh). Alternatively, install via [Homebrew, PyPI, or other methods](https://docs.astral.sh/uv/getting-started/installation/). ```md alert warning ## macOS (Apple Silicon) users: install Rosetta 2 Run `/usr/sbin/softwareupdate --install-rosetta --agree-to-license`. See [Apple's instructions](https://support.apple.com/en-us/HT211861) for details. ``` ### Set up the Reflex project Replace `` with your project name, then switch into the new directory. ```bash mkdir cd uv init uv add reflex uv run reflex init ``` ## Windows For Windows users, we recommend [WSL](https://learn.microsoft.com/en-us/windows/wsl/about) for optimal performance — WSL users should follow the macOS/Linux tab; the rest of this section covers native Windows. ### Install uv ```powershell powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` After installation, restart your terminal (PowerShell or Command Prompt). Alternatively, install via [WinGet, Scoop, or other methods](https://docs.astral.sh/uv/getting-started/installation/). ### Set up the Reflex project Replace `` with your project name, then switch into the new directory. ```powershell mkdir cd uv init uv add reflex uv run reflex init ``` ```md alert warning ## Error `Install Failed - You are missing a DLL required to run bun.exe` Windows Bun requires runtime components of Visual C++ libraries to run on Windows. This issue is fixed by installing [Microsoft Visual C++ 2015 Redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=53840). ``` ````` Running `uv run reflex init` will return the option to start with a blank Reflex app or to try our [AI builder](https://build.reflex.dev/). ```bash Initializing the web directory. Get started with a template: (0) A blank Reflex app. (1) Try our AI builder. Which template would you like to use? (0): ``` If this is your first time, pick **(0) A blank Reflex app** — the rest of the docs assume you started there. ## Run the App Run it in development mode: ```bash uv run reflex run ``` Your app runs at [http://localhost:3000](http://localhost:3000). Reflex _hot reloads_ any code changes in real time — your edits show up automatically. For troubleshooting, increase log verbosity with the `--loglevel` flag: ```bash uv run reflex run --loglevel debug ``` ```md alert info # Next: Build your first app Reflex is installed. The [Introduction](/docs/getting-started/introduction) walks through a working counter app in pure Python — the shortest path from "it runs" to "I understand it." ``` # Introduction Source: http://localhost:3000/docs/getting-started/introduction.md ```python exec import reflex as rx ``` **~5 min** · **Reflex** lets you build and deploy full-stack web apps — frontend, backend, and database — in **pure Python**. No JavaScript, no separate API, no context switching. ## Goals ```md section ### Pure Python Write your entire app — frontend, backend, database — in Python. No need to learn another language. ### Easy to Learn Ship your first app in minutes. No web development experience required. ### Full Flexibility Build anything from small data apps to large multi-page websites. **This entire site was built and deployed with Reflex.** ### Batteries Included One tool covers it all: UI, server-side logic, and deployment. ``` ## Build a counter We'll build a counter app that lets the user count up or down. In ~20 lines of Python you'll touch the three core pieces of every Reflex app: **state**, **event handlers**, and **components**. ```python exec class CounterExampleState(rx.State): count: int = 0 @rx.event def increment(self): self.count += 1 @rx.event def decrement(self): self.count -= 1 class IntroTabsState(rx.State): """The app state.""" value: str = "tab1" tab_selected: str = "" @rx.event def change_value(self, val: str): self.tab_selected = f"{val} clicked!" self.value = val def counter_code_section(code: str, tab: str) -> rx.Component: active = IntroTabsState.value == tab return rx.box( rx.code_block( code, class_name="code-block counter-code-block", ), background=rx.cond(active, "var(--c-violet-3)", "transparent"), border_left=rx.cond( active, "3px solid var(--c-violet-9)", "3px solid transparent", ), padding="0.875rem 1.5rem", class_name="w-full transition-colors", ) def counter_code_gap() -> rx.Component: return rx.box(height="0.875rem", flex_shrink="0") def tabs(): return rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Frontend", value="tab1", class_name="pill-tab"), rx.tabs.trigger("Backend", value="tab2", class_name="pill-tab"), rx.tabs.trigger("Page", value="tab3", class_name="pill-tab"), class_name="pill-tab-list", ), rx.tabs.content( rx.markdown( """The frontend is built declaratively with Reflex components, which compile to JS and run in the browser. Use `rx.cond` and `rx.foreach` instead of `if` and `for` for dynamic UIs. Any non-UI logic belongs in `State`. """, ), value="tab1", class_name="pt-4", ), rx.tabs.content( rx.markdown( """Write your backend in the `State` class. Here you can define functions and variables that can be referenced in the frontend. This code runs directly on the server and is not compiled, so there are no special caveats. Here you can use any Python external library and call any method/function. """, ), value="tab2", class_name="pt-4", ), rx.tabs.content( rx.markdown( """Each page is a Python function returning a Reflex component. Add as many as you want and link between them — see [Routing](/docs/pages/overview) for details. """, ), value="tab3", class_name="pt-4", ), class_name="text-secondary-12 font-normal", default_value="tab1", value=IntroTabsState.value, on_change=lambda x: IntroTabsState.change_value(x), ) ``` ```python demo box id=counter rx.hstack( rx.button( "Decrement", color_scheme="ruby", on_click=CounterExampleState.decrement, ), rx.heading(CounterExampleState.count, as_="h2", font_size="2em"), rx.button( "Increment", color_scheme="grass", on_click=CounterExampleState.increment, ), spacing="4", ) ``` Here is the full code for this example: ```python eval tabs() ``` ```python eval rx.box( counter_code_section("""import reflex as rx """, ""), counter_code_gap(), counter_code_section( """class State(rx.State): count: int = 0 @rx.event def increment(self): self.count += 1 @rx.event def decrement(self): self.count -= 1""", "tab2", ), counter_code_gap(), counter_code_section( """def index(): return rx.hstack( rx.button( "Decrement", color_scheme="ruby", on_click=State.decrement, ), rx.heading(State.count, font_size="2em"), rx.button( "Increment", color_scheme="grass", on_click=State.increment, ), spacing="4", )""", "tab1", ), counter_code_gap(), counter_code_section( """app = rx.App() app.add_page(index)""", "tab3", ), class_name=( "w-full flex flex-col overflow-hidden rounded-xl border " "border-secondary-4 bg-secondary-2 py-1" ), ) ``` ## The Structure of a Reflex App Let's break this example down. ### Import ```python import reflex as rx ``` Import Reflex as `rx`. All Reflex objects are accessed as `rx.*`. ### State ```python class State(rx.State): count: int = 0 ``` State holds the app's mutable data. Variables declared here are called **[vars](/docs/vars/base-vars)**. Our counter has one: `count`, starting at `0`. ### Event Handlers ```python @rx.event def increment(self): self.count += 1 @rx.event def decrement(self): self.count -= 1 ``` **Event handlers** are the only way to modify state. They're triggered by user actions (clicks, typing, etc.) — those actions are called **events**. Our counter has two handlers: `increment` and `decrement`. ### User Interface (UI) ```python def index(): return rx.hstack( rx.button( "Decrement", color_scheme="ruby", on_click=State.decrement, ), rx.heading(State.count, as_="h2", font_size="2em"), rx.button( "Increment", color_scheme="grass", on_click=State.increment, ), spacing="4", ) ``` The UI is built from components (`rx.hstack`, `rx.button`, `rx.heading`) that can be nested and styled with CSS or [Tailwind](/docs/styling/tailwind). Reflex ships with [50+ built-in components](/docs/library), and you can [wrap any React component](/docs/wrapping-react/overview). Components reference state vars (`rx.heading(State.count, …)`) and reactively re-render when state changes. Event triggers (`on_click=State.decrement`) wire UI to handlers. The sequence goes like this: 1. User clicks "Increment". 2. `on_click` fires. 3. `State.increment` runs on the server. 4. `State.count` is updated. 5. UI re-renders with the new value. ### Add pages ```python app = rx.App() app.add_page(index) ``` Create the app and register the page at the base route. ## Next Steps 🎉 You've built a fully interactive web app in pure Python. ```md alert info # Keep learning - [Dashboard tutorial](/docs/getting-started/dashboard-tutorial/) — build a real data app. - [Chatapp tutorial](/docs/getting-started/chatapp-tutorial/) — wire up streaming AI responses. - [How Reflex works](/docs/advanced-onboarding/how-reflex-works/) — what happens under the hood. ``` ```md alert info # Ship faster with AI - [Reflex Build](https://build.reflex.dev/) — generate a full app from a prompt. - [Reflex Cloud](/docs/hosting/deploy-quick-start/) — one-command deploy. ``` Press `Cmd+K` / `Ctrl+K` to search the docs. # Project Structure Source: http://localhost:3000/docs/getting-started/project-structure.md ## Directory Structure Let's create a new app called `hello` ```bash mkdir hello cd hello uv init uv add reflex uv run reflex init ``` This will create a directory structure like this: ```bash hello ├── .venv ├── .web ├── assets ├── hello │ ├── __init__.py │ └── hello.py ├── .gitignore ├── .python-version ├── pyproject.toml ├── rxconfig.py └── uv.lock ``` `uv init` may also create helper files such as `README.md`, `main.py`, and Git metadata. The tree above focuses on the main files you will interact with while building a Reflex app. Let's go over each of these directories and files. ## .venv `uv add reflex` creates a local virtual environment in `.venv` by default. This keeps your app dependencies isolated from the rest of your system Python. ## .web This is where the compiled Javascript files will be stored. You will never need to touch this directory, but it can be useful for debugging. Each Reflex page will compile to a corresponding `.js` file in the `.web/pages` directory. If Reflex installs frontend dependencies with Bun, the canonical `bun.lock` lives in a `reflex.lock/` directory at your project root and should be committed to version control. The dedicated directory keeps the file from clashing with a `bun.lock` from a user-managed bun project that may live alongside the Reflex project. Reflex mirrors `reflex.lock/bun.lock` into `.web` when it needs to run the package manager. ## Assets The `assets` directory is where you can store any static assets you want to be publicly available. This includes images, fonts, and other files. For example, if you save an image to `assets/image.png` you can display it from your app like this: ```python rx.image(src="https://web.reflex-assets.dev/other/image.png", alt="Example image asset") ``` ## Main Project Initializing your project creates a directory with the same name as your app. This is where you will write your app's logic. Reflex generates a default app within the `hello/hello.py` file. You can modify this file to customize your app. ## Python Project Files `pyproject.toml` defines your Python project metadata and dependencies. `uv add reflex` records the Reflex dependency there before you initialize the app. `uv.lock` stores the fully resolved dependency set for reproducible installs. Commit it to version control so everyone working on the app gets the same Python package versions. ## Configuration The `rxconfig.py` file can be used to configure your app. By default it looks something like this: ```python import reflex as rx config = rx.Config( app_name="hello", ) ``` We will discuss project structure and configuration in more detail in the [advanced project structure](/docs/advanced-onboarding/code-structure) documentation. # Project Source: http://localhost:3000/docs/hosting/adding-members.md ```python exec import reflex as rx ``` A project is a collection of applications (apps / websites). Every project has its own billing page that are accessible to Admins. ```md alert info # Looking for members, roles, and permissions? People, seats, roles, and access are organized at the organization and project level. For the complete guide, see [Organizations](/docs/ai/organization/overview/) — in particular [Members & seats](/docs/ai/organization/members/), [Roles & permissions](/docs/ai/organization/roles-and-permissions/), and [Managing project access](/docs/ai/organization/project-access/). ``` ## Adding Team Members To see the team members of a project click on the `Members` tab in the Cloud UI on the project page. If you are a User you have the ability to create, deploy and delete apps, but you do not have the power to add or delete users from that project. You must be an Admin for that. As an Admin you will see the an `Add user` button in the top right of the screen, as shown in the image below. Clicking on this will allow you to add a user to the project. You will need to enter the email address of the user you wish to add. ```python eval rx.image( src="https://web.reflex-assets.dev/other/hosting_adding_team_members.webp", alt="Adding team members to Reflex Cloud", ) ``` ```python eval rx.box(height="20px") ``` ```md alert warning # Currently a User must already have logged in once before they can be added to a project. At this time a User must be logged in to be added to a project. In future there will be automatic email invites sent to add new users who have never logged in before. ``` ## Other project settings Clicking on the `Settings` tab in the Cloud UI on the project page allows a user to change the `project name`, check the `project id` and, if the project is not your default project, delete the project. # App Source: http://localhost:3000/docs/hosting/app-management.md ```python exec import reflex as rx ``` In Reflex Cloud an "app" (or "application" or "website") refers to a web application built using the Reflex framework, which can be deployed and managed within the Cloud platform. You can deploy an app using the `reflex deploy` command. There are many actions you can take in the Cloud UI to manage your app. Below are some of the most common actions you may want to take. ## Stopping an App To stop an app follow the arrow in the image below and press on the `Stop app` button. Pausing an app will stop it from running and will not be accessible to users until you resume it. In addition, this will stop you being billed for your app. ```python eval rx.image( src="https://web.reflex-assets.dev/other/stopping_app.webp", alt="Stopping an app in Reflex Cloud", padding_bottom="20px", ) ``` ```md alert info # CLI Command to stop an app `reflex cloud apps stop [OPTIONS] [APP_ID]` ``` ## Deleting an App To delete an app click on the `Settings` tab in the Cloud UI on the app page. ```python eval rx.image( src="https://web.reflex-assets.dev/other/environment_variables.webp", alt="App environment variables in Reflex Cloud", ) ``` Then click on the `Danger` tab as shown below. ```python eval rx.image( src="https://web.reflex-assets.dev/other/deleting_app.webp", alt="Deleting an app in Reflex Cloud", ) ``` Here there is a `Delete app` button. Pressing this button will delete the app and all of its data. This action is irreversible. ```md alert info # CLI Command to delete an app `reflex cloud apps delete [OPTIONS] [APP_ID]` ``` ## Other app settings Clicking on the `Settings` tab in the Cloud UI on the app page also allows a user to change the `app name`, change the `app description` and check the `app id`. The other app settings also allows users to edit and add secrets (environment variables) to the app. For more information on secrets, see the [Secrets (Environment Variables)](/docs/hosting/secrets-environment-vars/) page. ## Deployment history Every `reflex deploy` creates a new deployment. List an app's deployment history — each deployment's status, Python/Reflex versions, VM type, optional description, and whether it can be rolled back to — with: ```bash reflex cloud apps history [APP_ID] ``` If you omit `APP_ID`, the app is resolved from the `appid` in your `cloud.yml`/`pyproject.toml`, or you can pass `--app-name`. Add `--json` for machine-readable output. ## Deployment descriptions You can attach a short changelog note to a deployment so your history is easy to scan (for example, `"bump pricing page copy"`). Set it at deploy time: ```bash reflex deploy --description "bump pricing page copy" ``` You can also set or replace the note on an existing deployment. Find the deployment id with `reflex cloud apps history`, then: ```bash reflex cloud apps describe --app-id --description "hotfix: revert checkout change" ``` Pass `--description ""` to clear the note. Descriptions show up in `reflex cloud apps history` and in the Cloud dashboard. ```md alert info # CLI command to set a deployment description `reflex cloud apps describe [OPTIONS] DEPLOYMENT_ID --description ""` ``` ## Rolling back a deployment If a deploy introduces a regression, you can roll back to any previous deployment that still has a built image. A rollback redeploys that deployment's existing image — no rebuild from source — and makes it current again, demoting the currently running deployment to history. ```bash reflex cloud apps rollback --app-id ``` Use `reflex cloud apps history` to find rollback-eligible deployments: their `can rollback` value is `True`. A deployment can only be rolled back to on the provider whose registry holds its image, so after [switching an app's provider](/docs/hosting/cloud-providers/) the deployments built on the previous provider are no longer offered as rollback targets. ```md alert info # CLI command to roll back `reflex cloud apps rollback [OPTIONS] DEPLOYMENT_ID` ``` # Billing Source: http://localhost:3000/docs/hosting/billing.md ```python exec import reflex as rx ``` ```md alert info # Members, seats, and roles Seats, members, and roles are managed in your organization's settings. See [Members & seats](/docs/ai/organization/members/) and [Roles & permissions](/docs/ai/organization/roles-and-permissions/) in the [Organizations](/docs/ai/organization/overview/) section. ``` ## Overview Billing for Reflex Cloud is monthly per project. Project owners and admins are able to view and manage the billing page. The billing for a project is comprised of two parts - number of `seats` and `compute`. ## Seats Projects on a paid plan can invite collaborators to join their project. Each additional collaborator is considered a `seat` and is charged on a flat monthly rate. Project owners and admins can manage permissions and roles for each seat in the settings tab on the project page. ## Compute Reflex Cloud is billed on a per second basis so you only pay for when your app is being used by your end users. When your app is idle, you are not charged. For more information on compute pricing, please see the [compute](/docs/hosting/compute/) page. ## Manage Billing To manage your billing, you can go to the `Billing` tab in the Cloud UI on the project page. ## Setting Billing Limits If you want to set a billing limit for your project, you can do so by going to the `Billing` tab in the Cloud UI on the project page. # Bring Your Own Cloud Source: http://localhost:3000/docs/hosting/bring-your-own-cloud.md Bring Your Own Cloud (BYOC) lets you deploy Reflex apps to your own **AWS**, **GCP**, or **Azure** account with the same `reflex cloud deploy` command you already use. Everything runs inside your account: the build runs on your cloud's builders, the image is pushed to your internal registry, and the app runs on your cloud's managed runtime. Reflex never holds standing credentials into your account, and nothing about the app or its data leaves your perimeter. ```md alert info # Enterprise tier only. BYOC is part of the **Enterprise tier** of Reflex Cloud. Contact [sales@reflex.dev](mailto:sales@reflex.dev) to upgrade. ``` ## Why BYOC Reflex Cloud takes you from a Python file to a live app in one command, but that app runs on Reflex's infrastructure — a non-starter for teams whose cloud is fixed by security review, procurement, or data-residency policy. If the deployment target isn't approved, the app doesn't ship. The alternative is to build the deploy yourself on Cloud Run, ECS, or Container Apps: a Dockerfile, a build pipeline, a registry, runtime config, infrastructure-as-code, and ongoing maintenance. That's its own engineering project, and most of it has nothing to do with your app. BYOC keeps the Reflex Cloud workflow your team already uses — app lifecycle, autoscaling, environment variables, and the deploy CLI all behave the way they always have. The only thing that changes is where the container ends up running. ## Deploy targets The same `reflex cloud deploy` command works against each cloud with a single flag: ```bash reflex cloud deploy --aws reflex cloud deploy --gcp reflex cloud deploy --azure ``` | Cloud | Flag | Managed runtime | | --- | --- | --- | | AWS | `--aws` | ECS | | GCP | `--gcp` | Cloud Run | | Azure | `--azure` | Container Apps | ## How the deploy command works Running `reflex cloud deploy --` kicks off a short interactive flow: 1. **Authenticate.** The CLI checks that your cloud's own CLI (`aws`, `gcloud`, or `az`) is installed and that you're logged in, and walks you through `aws configure`, `gcloud auth login`, or `az login` if you're not. From there everything runs under your credentials. 2. **Configure.** It pulls the latest Reflex Cloud config for your app and prompts you for any flags it needs. 3. **Review.** Before anything runs, it prints the exact build and deploy commands it's about to execute and asks for approval. 4. **Deploy.** On approval, it builds the container image with your cloud's native builders, pushes it to your internal artifact registry, and deploys the app to the managed runtime. The final output is the URL of the live app, running in your own account. The whole flow takes about three minutes the first time, and under a minute on subsequent deploys. ## What's in scope today BYOC is generally available on AWS, GCP, and Azure for Reflex Enterprise customers. It supports: - Reflex apps on Cloud Run, ECS, and Container Apps - Authentication through your existing cloud CLI - The standard set of Reflex Cloud configuration flags ## Next steps - For a detailed walkthrough of the GCP target — prerequisites, options, the security model, and troubleshooting — see [Deploy to GCP Cloud Run](/docs/hosting/deploy-to-gcp/). - If you're on Reflex Enterprise, update the CLI and run `reflex cloud deploy --`. - If you're not, see [reflex.dev/pricing](https://reflex.dev/pricing/) for the comparison and a demo link. # Cloud Providers Source: http://localhost:3000/docs/hosting/cloud-providers.md ```python exec import reflex as rx ``` By default, `reflex deploy` runs your app on Reflex Cloud's managed infrastructure. If your organization has connected its own cloud account, you can instead deploy the same app — with the same `reflex deploy` command and the same managed lifecycle (logs, scaling, history, rollbacks) — into **your own cloud account**. Today this is supported for **Google Cloud (GCP)**, where your app runs on [Cloud Run](https://cloud.google.com/run) in your GCP project. ```md alert info # Enterprise tier only. Deploying to a connected cloud provider is part of the **Enterprise tier** of Reflex Cloud. Contact [sales@reflex.dev](mailto:sales@reflex.dev) to upgrade. ``` ```md alert warning # Managed vs. self-service GCP deploys This page covers the **managed** flow: an admin connects a GCP account to your organization once, and Reflex Cloud deploys into it for you using the normal `reflex deploy` command, giving you the full managed lifecycle (history, rollback, scaling, logs). This is different from the self-service [`reflex cloud deploy --gcp`](/docs/hosting/deploy-to-gcp/) command, which builds and deploys from your own machine using your local `gcloud`. ``` ## Connecting Google Cloud An organization admin connects the GCP account once, from the Reflex Cloud dashboard under **Organization → Cloud Providers**. You provide a service-account key, your GCP project number, and a region; Reflex validates that the service account has the permissions needed to deploy Cloud Run services and push images to Artifact Registry before storing the (encrypted) credentials. Once connected, check availability from the CLI: ```bash reflex cloud providers status ``` This reports whether GCP is connected, whether your plan allows GCP deploys, and the connected project and region. List all connected provider accounts with: ```bash reflex cloud providers list ``` ## Choosing a provider at deploy time When your org has GCP connected, `reflex deploy` asks where you want to deploy: ```console $ reflex deploy This organization has Google Cloud connected (region us-central1). Where would you like to deploy? [reflex-cloud/gcp] (reflex-cloud): ``` Choose `reflex-cloud` for Reflex's managed infrastructure or `gcp` for your connected Google Cloud account. To skip the prompt (for example in CI), pass `--provider`: ```bash reflex deploy --provider gcp reflex deploy --provider reflex-cloud ``` You can also pin the provider in your `cloud.yml` / `pyproject.toml` so every deploy targets the same place: ```yaml provider: gcp ``` When you deploy to GCP, the region and machine sizing come from the connected GCP account, so `--region` and `--vmtype` are ignored. ## Switching providers An app remembers its provider between deploys. You can switch it at any time by choosing a different provider on your next `reflex deploy` (or with `--provider`). Switching a **deployed** app tears down its resources on the previous provider and requires a redeploy to come back up on the new one — `reflex deploy` performs that redeploy as part of the switch. In interactive mode the CLI warns and asks for confirmation before switching a deployed app. ## What runs where | | Reflex Cloud | Google Cloud (managed) | | --- | --- | --- | | Runtime | Reflex-managed | Cloud Run in your GCP project | | Compute billing | Reflex Cloud | Your GCP account | | Region / sizing | `--region` / `--vmtype` | From the connected GCP account | | Logs, history, rollback, scaling | ✅ | ✅ | Deployment descriptions and rollbacks work the same way on both providers — see [App management](/docs/hosting/app-management/). # Compute Source: http://localhost:3000/docs/hosting/compute.md ```python exec import reflex as rx ``` ## Compute Usage Reflex Cloud is billed on a per second basis so you only pay for when your app is being used by your end users. When your app is idle, you are not charged. This allows you to deploy your app on larger sizes and multiple regions without worrying about paying for idle compute. We bill on a per second basis so you only pay for the compute you use. By default your app stays alive for 5 minutes after the no users are connected. After this time your app will be considered idle and you will not be charged. Start up times usually take less than 1 second for you apps to come back online. #### Warm vs Cold Start - Apps below `c2m2` are considered warm starts and are usually less than 1 second. - If your app is larger than `c2m2` it will be a cold start which takes around 15 seconds. If you want to avoid this you can reserve a machine. ## Reserved Machines (Coming Soon) If you expect your apps to be continuously receiving users, you may want to reserve a machine instead of having us manage your compute. This will be a flat monthly rate for the machine. ## Monitoring Usage To monitor your projects usage, you can go to the billing tab in the Reflex Cloud UI on the project page. Here you can see the current billing and usage for your project. ## Real Life Examples of compute charges on the paid tiers ```md alert # Single Application - Single Region Anna, a hobbyist game developer, built a pixel art generator and hosted it on Reflex Cloud so fellow artists could use it anytime. She deployed her app in the San Francisco region, where she lives. If her users use the site for an hour a day, how much would Anna pay? **Facts:** - **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour - **Regions:** `1` (SJC) - **Avg usage per day per region:** `1 Hour` **Maths:** `1 region * 1 hour * 30 days = 30 compute hours` `30 * 0.083 = 2.49` (assuming a 30 day month) Anna's total cost for compute would be `$2.49` for the month. However, since paid users receive a `$10` credit, her compute cost is fully covered. **Charge for compute:** `$0.00 dollars` ``` ```md alert # Single Application - Multi Region Bob created a social media application and decided to host it on Reflex Cloud. Bob has users in Paris, London, San Jose and Sydney. Bob decided to deploy his application to all those region as well as additional one in Paris since that where Bob lives. If users use the site in each region for 30 minutes a day how much would Bob pay? **Facts:** - **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` Cost per hour - **Regions:** `5` (CDG x 2, LHR x 1, SJC x 1, SYD x 1) - **Avg usage per day per region:** `0.5 Hours` **Maths:** `5 regions * 0.5 hours * 30 days = 75 compute hours` `75 * 0.083 = 6.23` (assuming a 30 day month) Bob would owe `$6.23` for this month. However since Bob is a paid user they receive a `$10` credit which brings Bob's bill down to `$0`. **Charge for compute:** `$0.00 dollars` ``` ```md alert # Single Growing Application - Multi Region Charlie, a small startup founder, built a finance tracking app that allows users to create and share finance insights in real time. As his user base expanded across different regions, he needed a multi-region setup to reduce latency and improve performance. To ensure a smooth experience, he deployed his app on Reflex Cloud using a `c1m2` machine in four regions. If users access the app on average for **16 hours per week** in each region, how much would Charlie pay? **Facts:** - **Machine size:** `c1m2` (1 CPU, 2 GB Memory) - `$0.157` per hour - **Regions:** `4` - **Avg usage per week per region:** `16 Hours` **Maths:** `4 regions * 16 hours * 4 weeks = 256 compute hours` `256 * 0.157 = 40.19` (assuming 4 weeks in a month) Charlie would owe `$40.19` for this month. However since Charlie is a paid user they receive a `$10` credit which brings Bob's bill down to `$30.19`. **Charge for compute:** `$30.19 dollars` ``` ```md alert # Single Application High-Performance App - Single Region David, an **AI enthusiast**, developed a **real-time image enhancement tool** that allows photographers to upscale and enhance their images using machine learning. Since his app requires more processing power, he deployed it on a **`c2m2` machine**, which offers increased CPU and memory to handle the intensive AI workloads. With users accessing the app **2 hours per day** over a **30-day month**, how much would David pay? **Facts:** - **Machine size:** `c2m2` (2 CPU, 2 GB Memory) - `$0.166` per hour - **Regions:** `1` - **Avg usage per day:** `2 Hours` **Maths:** `1 region * 2 hours * 30 days = 60 compute hours` `60 * 0.166 = 9.96` (assuming a 30 day month) David would owe `$9.96` for this month. However since David is a paid user they receive a `$10` credit, he will not be charged for compute for this month. **Charge for compute:** `$0.00 dollars` ``` ```md alert # Single Fast Scaling App - Multiple Regions Emily, a **productivity app developer**, built a **real-time team collaboration tool** that helps remote teams manage tasks and communicate efficiently. With users spread across multiple locations, she needed **low-latency performance** to ensure a seamless experience. To achieve this, Emily deployed her app using a `c1m1` machine in **three regions**. With users actively using the app **6 hours per day in each region** over a **30-day month**, how much would Emily pay? **Facts:** - **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour - **Regions:** `3` - **Avg usage per day per region:** `6 Hours` **Maths:** `3 regions * 6 hours * 30 days = 540 compute hours` `540 * 0.083 = 44.82` (assuming a 30 day month) Emily would owe `$44.82` for this month. However since Emily is a paid user they receive a `$10` credit which brings Emily's bill down to `$34.82`. **Charge for compute:** `$34.82 dollars` ``` ```md alert # Multiple Apps - Multiple Regions Fred, a **freelance developer**, built a **portfolio of web applications** that cater to different clients across the globe. He has built **5 apps** where **4 apps** have a small amount of traffic with an average of **0.5 hours a day** and **1 app** that has a high amount of traffic with an average of **6 hours** a day. He has deployed the 4 small traffic apps on a `c1m1` machine in **1 region** each and the high traffic app on a `c1m1` machine in **2 regions**. How much would Fred pay? **Facts for 4 small traffic apps:** - **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour - **Regions:** `1` - **Avg usage per day per region:** `0.5 Hours` **Facts for 1 large traffic app:** - **Machine size:** `c1m1` (1 CPU, 1 GB Memory) - `$0.083` per hour - **Regions:** `2` - **Avg usage per day per region:** `6 Hours` **Maths:** 4 small traffic apps: `4 apps * 1 region * 0.5 hours * 30 days = 60 compute hours` 1 large traffic apps: `2 regions * 6 hours * 30 days = 360 compute hours` Total compute hours = `60 + 360 = 420 compute hours` `420 * 0.083 = 34.86` (assuming a 30 day month) Fred would owe `$34.86` for this month. However since Fred is a paid user they receive a `$10` credit which brings Fred's bill down to `$24.86`. **Charge for compute:** `$24.82 dollars` ``` One thing that is important to note is that in the hypothetical example where you have `50 people` using your app `continuously for 24 hours` or if you have `1 person` using your app `continuously for 24 hours`, you `will be charged the same amount` as the charge is based on the amount of time your app up and not the number of users using your app. In `both these examples` your `app is up for 24 hours` and therefore you will be `charged for 24 hours of compute`. # Cloud Config File Source: http://localhost:3000/docs/hosting/config-file.md ```python exec import reflex as rx ``` ## What is reflex cloud config? The following command: ```bash reflex cloud config ``` generates a `cloud.yml` configuration file used to deploy your Reflex app to the Reflex cloud platform. This file tells Reflex how and where to run your app in the cloud. ## Configuration File Structure The `cloud.yml` file uses YAML format and supports the following structure. **All fields are optional** and will use sensible defaults if not specified: ```yaml # Basic deployment settings name: my-app-prod # Optional: defaults to project folder name description: 'Production deployment' # Optional: empty by default projectname: my-client-project # Optional: defaults to personal project # Infrastructure settings regions: # Optional: defaults to sjc: 1 sjc: 1 # San Jose (# of machines) lhr: 2 # London (# of machines) vmtype: c2m2 # Optional: defaults to c1m1 # Custom domain and environment hostname: myapp # Optional: myapp.reflex.dev envfile: .env.production # Optional: defaults to .env # Additional dependencies packages: # Optional: empty by default - procps ``` ## Configuration Options Reference ```python demo-only rx.table.root( rx.table.header( rx.table.row( rx.table.column_header_cell( rx.text("Option", size="1", weight="bold", color=rx.color("slate", 11)) ), rx.table.column_header_cell( rx.text("Type", size="1", weight="bold", color=rx.color("slate", 11)) ), rx.table.column_header_cell( rx.text("Default", size="1", weight="bold", color=rx.color("slate", 11)) ), rx.table.column_header_cell( rx.text( "Description", size="1", weight="bold", color=rx.color("slate", 11) ) ), align="center", ) ), rx.table.body(*[ rx.table.row( rx.table.cell(rx.text(option, class_name="text-sm")), rx.table.cell(rx.text(type_, class_name="text-sm")), rx.table.cell(rx.text(default, class_name="text-sm")), rx.table.cell( rx.link(description, href=link, class_name="text-sm") if link else rx.text(description, size="1", weight="regular") ), align="center", ) for option, type_, default, description, link in [ ( "name", "string", "folder name", "Deployment identifier in dashboard", None, ), ("description", "string", "empty", "Description of deployment", None), ( "regions", "object", "sjc: 1", "Region deployment mapping", "/docs/hosting/regions", ), ( "vmtype", "string", "c1m1", "Virtual machine specifications", "/docs/hosting/machine-types", ), ("hostname", "string", "null", "Custom subdomain", None), ( "envfile", "string", ".env", "Environment variables file path", "/docs/hosting/secrets-environment-vars", ), ("project", "uuid", "null", "Project uuid", None), ("projectname", "string", "null", "Project name", None), ("packages", "array", "empty", "Additional system packages", None), ("include_db", "boolean", "false", "Include local sqlite", None), ("strategy", "string", "auto", "Deployment strategy", None), ] ]), variant="ghost", size="2", width="100%", max_width="800px", ) ``` ## Configuration Options For details of specific sections click the links in the table. ### Projects Organize deployments using projects: ```yaml projectname: client-alpha # Groups related deployments ``` You can also specify a project uuid instead of name: ```yaml project: 12345678-1234-1234-1234-1234567890ab ``` You can go to the homepage of the project in the reflex cloud dashboard to find your project uuid in the url `https://build.reflex.dev/project/uuid` ### Apt Packages Install additional system packages your application requires. Package names are based on the apt package manager: ```yaml packages: - procps=2.0.32-1 # Version pinning is optional - imagemagick - ffmpeg ``` ### Include SQLite Include local sqlite database: ```yaml include_db: true ``` This is not persistent and will be lost on restart. It is recommended to use a database service instead. ### Strategy Deployment strategy: Available strategies: - `immediate`: [Default] Deploy immediately - `rolling`: Deploy in a rolling manner - `bluegreen`: Deploy in a blue-green manner - `canary`: Deploy in a canary manner, boot as single machine verify its health and then restart the rest. ```yaml strategy: immediate ``` ## Multi-Environment Setup **Development (`cloud-dev.yml`):** ```yaml name: myapp-dev description: 'Development environment' vmtype: c1m1 envfile: .env.development ``` **Staging (`cloud-staging.yml`):** ```yaml name: myapp-staging description: 'Staging environment' regions: sjc: 1 vmtype: c2m2 envfile: .env.staging ``` **Production (`cloud-prod.yml`):** ```yaml name: myapp-production description: 'Production environment' regions: sjc: 2 lhr: 1 vmtype: c4m4 hostname: myapp envfile: .env.production ``` Deploy with specific configuration files: ```bash # Use default cloud.yml reflex deploy # Use specific configuration file reflex deploy --config cloud-prod.yml reflex deploy --config cloud-staging.yml ``` # Custom Domains Source: http://localhost:3000/docs/hosting/custom-domains.md ```python exec import reflex as rx ``` With the Enterprise tier of Reflex Cloud you can use your own custom domain to host your app. ## Prerequisites You must purchase a domain from a domain registrar such as GoDaddy, Cloudflare, Namecheap, or AWS. For this tutorial we will use GoDaddy and the example domain `tomgotsman.us`. ## Steps Once you have purchased your domain, you can add it to your Reflex Cloud app by following these steps: 1 - Ensure you have deployed your app to Reflex Cloud. 2 - Once your app is deployed click the `Custom Domain` tab and add your custom domain to the input field and press the Add domain button. You should now see a page like below: ```python eval rx.image( src="https://web.reflex-assets.dev/other/custom-domains-DNS-inputs.webp", alt="Custom domain DNS record inputs", ) ``` ```python eval rx.box(height="20px") ``` 3 - On the domain registrar's website, navigate to the DNS settings for your domain. It should look something like the image below: ```python eval rx.image( src="https://web.reflex-assets.dev/other/custom-domains-DNS-before.webp", alt="DNS settings before adding custom domain records", ) ``` ```python eval rx.box(height="20px") ``` 4 - Add all four of the DNS records provided by Reflex Cloud to your domain registrar's DNS settings. If there is already an A name record, delete it and replace it with the one provided by Reflex Cloud. Your DNS settings should look like the image below: ```python eval rx.image( src="https://web.reflex-assets.dev/other/custom-domains-DNS-after.webp", alt="DNS settings after adding custom domain records", ) ``` ```md alert warning # It may alert you that this record will resolve on ######.tomgotsman.us.tomgotsman.us. If this happens ensure that you select to only have the record resolve on ######.tomgotsman.us. ``` ```md alert warning # Your domain provider may not support an Apex CNAME record, in this case just use an A record. ![Image showing failed CNAME record](/custom-domains-CNAME-fail.png) ``` ```python eval rx.box(height="20px") ``` 5 - Once you have added the DNS records, refresh the page on the Reflex Cloud page (it may take a few minutes to a few hours to update successfully). If the records are correct, you should see a success message like the one below: ```python eval rx.image( src="https://web.reflex-assets.dev/other/custom-domains-success.webp", alt="Custom domain successfully configured", ) ``` ```python eval rx.box(height="20px") ``` 6 - Now redeploy your app using the `reflex deploy` command and your app should now be live on your custom domain! # Deploy Reflex to Databricks Source: http://localhost:3000/docs/hosting/databricks.md ```python exec import reflex as rx ``` This guide walks you through deploying a Reflex web application on Databricks using the Apps platform. ## Prerequisites - Databricks workspace with Unity Catalog enabled - GitHub repository containing your Reflex application - Reflex Enterprise license (for single-port deployment) ## Step 1: Connect Your Repository 1. **Link GitHub Repository** - Navigate to your Databricks workspace - Go to your user directory - Click **Create** → **Git folder** - Paste the URL of your GitHub repository containing the Reflex application ## Step 2: Configure Application Settings ### Create Configuration File Create a new file called `app.yaml` directly in Databricks (not in GitHub): ```yaml command: [ "reflex", "run", "--env", "prod", "--backend-port", "$DATABRICKS_APP_PORT" ] env: - name: "HOME" value: "/tmp/reflex" - name: "REFLEX_ACCESS_TOKEN" value: "your-token-here" - name: "DATABRICKS_WAREHOUSE_ID" value: "your-sql-warehouse-id" - name: "DATABRICKS_CATALOG" value: "your-catalog-name" - name: "DATABRICKS_SCHEMA" value: "your-schema-name" - name: "REFLEX_SHOW_BUILT_WITH_REFLEX" value: 0 ``` ### Obtain Required Tokens 1. **Reflex Access Token** - Visit the [Reflex Cloud Tokens docs](/docs/hosting/tokens) - Navigate to Account Settings → Tokens - Create a new token and copy the value - Replace `your-token-here` in the configuration 2. **Databricks Resources** - Update `DATABRICKS_WAREHOUSE_ID` with your SQL warehouse ID - Update `DATABRICKS_CATALOG` with your target catalog name - Update `DATABRICKS_SCHEMA` with your target schema name ## Step 3: Enable Single-Port Deployment Update your Reflex application for Databricks compatibility: ### Update rxconfig.py ```python import reflex as rx import reflex_enterprise as rxe rxe.Config(app_name="app", use_single_port=True) ``` ### Update Application Entry Point Modify your main application file where you define `rx.App`: ```python import reflex_enterprise as rxe app = rxe.App( # your app configuration ) ``` ```md alert info # Also add `reflex-enterprise` and `asgiproxy` to your `requirements.txt` file. ``` ## Step 4: Create Databricks App 1. **Navigate to Apps** - Go to **Compute** → **Apps** - Click **Create App** 2. **Configure Application** - Select **Custom App** - Configure SQL warehouse for your application ## Step 5: Set Permissions If you are using the `samples` Catalog then you can skip the permissions section. ### Catalog Permissions 1. Navigate to **Catalog** → Select your target catalog 2. Go to **Permissions** 3. Add the app's service principal user 4. Grant the following permissions: - **USE CATALOG** - **USE SCHEMA** ### Schema Permissions 1. Navigate to the specific schema 2. Go to **Permissions** 3. Grant the following permissions: - **USE SCHEMA** - **EXECUTE** - **SELECT** - **READ VOLUME** (if required) ## Step 6: Deploy Application 1. **Initiate Deployment** - Click **Deploy** in the Apps interface - When prompted for the code path, provide your Git folder path or select your repository folder 2. **Monitor Deployment** - The deployment process will begin automatically - Monitor logs for any configuration issues ## Updating Your Application To deploy updates from your GitHub repository: 1. **Pull Latest Changes** - In the deployment interface, click **Deployment Source** - Select **main** branch - Click **Pull** to fetch the latest changes from GitHub 2. **Redeploy** - Click **Deploy** again to apply the updates ## Configuration Reference ```python eval rx.table.root( rx.table.header( rx.table.row( rx.table.column_header_cell("Environment Variable"), rx.table.column_header_cell("Description"), rx.table.column_header_cell("Example"), ), ), rx.table.body( rx.table.row( rx.table.cell(rx.code("HOME")), rx.table.cell("Application home directory"), rx.table.cell(rx.code("/tmp/reflex")), ), rx.table.row( rx.table.cell(rx.code("REFLEX_ACCESS_TOKEN")), rx.table.cell("Authentication for Reflex Cloud"), rx.table.cell(rx.code("rx_token_...")), ), rx.table.row( rx.table.cell(rx.code("DATABRICKS_WAREHOUSE_ID")), rx.table.cell("SQL warehouse identifier"), rx.table.cell("Auto-assigned"), ), rx.table.row( rx.table.cell(rx.code("DATABRICKS_CATALOG")), rx.table.cell("Target catalog name"), rx.table.cell(rx.code("main")), ), rx.table.row( rx.table.cell(rx.code("DATABRICKS_SCHEMA")), rx.table.cell("Target schema name"), rx.table.cell(rx.code("default")), ), rx.table.row( rx.table.cell(rx.code("REFLEX_SHOW_BUILT_WITH_REFLEX")), rx.table.cell("Show Reflex branding (Enterprise only)"), rx.table.cell([rx.code("0"), " or ", rx.code("1")]), ), ), variant="surface", margin_y="1em", ) ``` ## Troubleshooting - **Permission Errors**: Verify that all catalog and schema permissions are correctly set - **Port Issues**: Ensure you're using `$DATABRICKS_APP_PORT` and single-port configuration - **Token Issues**: Verify your Reflex access token is valid and properly configured - **Deployment Failures**: Check the deployment logs for specific error messages ## Notes - Single-port deployment requires Reflex Enterprise - Configuration must be created directly in Databricks, not pushed from GitHub - Updates require manual pulling from the deployment interface # Reflex Cloud - Quick Start Source: http://localhost:3000/docs/hosting/deploy-quick-start.md ```python exec import reflex as rx ``` So far, we have been running our apps locally on our own machines. But what if we want to share our apps with the world? This is where the hosting service comes in. ## Quick Start Reflex’s hosting service makes it easy to deploy your apps without worrying about configuring the infrastructure. ### Prerequisites 1. Hosting service requires `reflex>=0.6.6`. 2. This tutorial assumes you have successfully `reflex init` and `reflex run` your app. 3. Also make sure you have a `requirements.txt` file at the top level app directory that contains all your python dependencies! (To create a `requirements.txt` file, run `pip freeze > requirements.txt`.) ### Authentication First run the command below to login / signup to your Reflex Cloud account: (command line) ```bash reflex login ``` You will be redirected to your browser where you can authenticate through Github or Gmail. ### Web UI Once you are at this URL and you have successfully authenticated, click on the one project you have in your workspace. You should get a screen like this: ```python eval rx.image( src="https://web.reflex-assets.dev/other/cloud_project_page.webp", alt="Reflex Cloud Dashboard", ) ``` This screen shows the login command and the deploy command. As we are already logged in, we can skip the login command. ### Deployment Now you can start deploying your app. In your cloud UI copy the `reflex deploy` command similar to the one shown below. ```bash reflex deploy --project 2a432b8f-2605-4753-####-####0cd1#### ``` In your project directory (where you would normally run `reflex run`) paste this command. The command is by default interactive. It asks you a few questions for information required for the deployment. 1. The first question will compare your `requirements.txt` to your python environment and if they are different then it will ask you if you want to update your `requirements.txt` or to continue with the current one. If they are identical this question will not appear. To create a `requirements.txt` file, run `pip freeze > requirements.txt`. 2. The second question will search for a deployed app with the name of your current app, if it does not find one then it will ask if you wish to proceed in deploying your new app. 3. The third question is optional and will ask you for an app description. That’s it! You should receive some feedback on the progress of your deployment and in a few minutes your app should be up. 🎉 For detailed information about the deploy command and its options, see the [Deploy API Reference](/docs/hosting/deploy-quick-start/) and the [CLI Reference](https://reflex.dev/docs/api-reference/cli/). ```md alert info # Once your code is uploaded, the hosting service will start the deployment. After a complete upload, exiting from the command **does not** affect the deployment process. The command prints a message when you can safely close it without affecting the deployment. ``` If you go back to the Cloud UI you should be able to see your deployed app and other useful app information. ```md alert info # Setup a Cloud Config File To create a `config.yml` file for your app to set your app configuration check out the [Cloud Config Docs](/docs/hosting/config-file/). ``` ```md alert info # Moving around the Cloud UI To go back, i.e. from an app to a project or from a project to your list of projects you just click the `REFLEX logo` in the top left corner of the page. ``` ```md alert info # All flag values are saved between runs All your flag values, i.e. environment variables or regions or tokens, are saved between runs. This means that if you run a command and you pass a flag value, the next time you run the same command the flag value will be the same as the last time you ran it. This means you should only set the flag values again if you want to change them. ``` # Deploy to GCP Source: http://localhost:3000/docs/hosting/deploy-to-gcp.md ```python exec import reflex as rx ``` The `reflex cloud deploy --gcp` command deploys a Reflex app to your own [Google Cloud Run](https://cloud.google.com/run) service. Reflex Cloud fetches a Cloud Run-ready Dockerfile and a `gcloud` deploy script, wraps the Dockerfile inside a [Cloud Build config (`cloudbuild.yaml`)](https://cloud.google.com/build/docs/build-config-file-schema), and runs the script against the Google Cloud project you specify. The image is built on Cloud Build (so it works from any host OS, including Apple Silicon) and pushed to Artifact Registry. Your project tree is never modified — the Dockerfile lives only inside the build config that's submitted to Cloud Build. ```md alert info # Enterprise tier only. Self-deploying to GCP Cloud Run is part of the **Enterprise tier** of Reflex Cloud. The control plane will return `403` to non-Enterprise tokens, and the CLI surfaces a clear error pointing at this. Contact [sales@reflex.dev](mailto:sales@reflex.dev) to upgrade. ``` ```md alert warning # Self-service vs. managed GCP deploys This page covers the **self-service** `reflex cloud deploy --gcp` command, which builds and deploys from your own machine using your local `gcloud`. If you'd rather connect a GCP account to your organization once and deploy with the normal `reflex deploy` command — keeping the managed lifecycle (history, rollback, scaling, logs) — see [Cloud Providers](/docs/hosting/cloud-providers/). ``` ## Prerequisites Before running the command, install and authenticate the local tools the deploy script invokes: - `gcloud` — install from the [Google Cloud SDK docs](https://cloud.google.com/sdk/docs/install), then run: - `gcloud auth login` - `gcloud auth application-default login` - `docker` — required by `gcloud builds submit` for source upload. - `bash` — used to run the deploy script. You also need: - A GCP project with **billing enabled**. Without it, `gcloud services enable` fails with `UREQ_PROJECT_BILLING_NOT_FOUND`. - An Enterprise-tier Reflex Cloud subscription and a logged-in Reflex CLI (`reflex login`). ## Quick start From the root of your Reflex app: ```bash reflex cloud deploy --gcp \ --gcp-project my-gcp-project-id \ --service-name my-reflex-app ``` The CLI will: 1. Authenticate against Reflex Cloud and fetch the deploy manifest (Dockerfile + `gcloud` script). 2. Generate a `cloudbuild.yaml` that embeds the Dockerfile as a build step, write it to a tempfile, and rewrite the script's `gcloud builds submit` invocation to use `--config="$REFLEX_CLOUDBUILD_YAML"`. 3. Print the (rewritten) script so you can review it. 4. Ask for confirmation, then run the script with `cwd=` your source directory: enable the required APIs, create the Artifact Registry repository, build the image on Cloud Build (which materializes the Dockerfile inside the build step from the `cloudbuild.yaml`), and deploy a public Cloud Run service. 5. Delete the tempfile after the script finishes. Your source tree is never written to — if you have an existing `Dockerfile` in `--source`, it's left in place and ignored. The Reflex-provided Dockerfile only exists inside the `cloudbuild.yaml` tempfile (and inside the Cloud Build job). When it's done, you'll get a service URL like `https://my-reflex-app-.us-central1.run.app`. ## Options | Option | Default | Description | | --- | --- | --- | | `--gcp` | _(required)_ | Selects the GCP Cloud Run target. | | `--gcp-project` | _(required)_ | The GCP **project ID** to deploy into. Project numbers are **not** accepted by `gcloud artifacts repositories`; use the project ID. | | `--region` | `us-central1` | Cloud Run region. | | `--service-name` | `reflex-app` | Cloud Run service name. | | `--ar-repo` | `reflex` | Artifact Registry repository name (created on first deploy). | | `--version` | UTC timestamp (`YYYYMMDD-HHMMSS`) | Image version tag. | | `--source` | `.` | Directory containing the Reflex app. Uploaded to Cloud Build as the build context; the source tree itself is not modified. | | `--token` | _from `~/.reflex` config_ | Reflex authentication token. | | `--interactive / --no-interactive` | `--interactive` | Whether to prompt before running the deploy script. | | `--dry-run` | _off_ | Print the manifest, the generated `cloudbuild.yaml`, and the rewritten script without writing the tempfile or running the script. | | `--loglevel` | `info` | Log verbosity. | ## What gets created in your GCP project The deploy script enables these APIs (if not already enabled): - `cloudbuild.googleapis.com` - `run.googleapis.com` - `artifactregistry.googleapis.com` It then creates (idempotently) and uses: - An Artifact Registry Docker repository at `${REGION}-docker.pkg.dev/${GCP_PROJECT}/${AR_REPO}`. - A Cloud Build job that builds and pushes the image. - A Cloud Run service named `${SERVICE_NAME}`, deployed with `--allow-unauthenticated`, port 8080, 1 vCPU, 1 GiB memory, `--min-instances 1`, and `--session-affinity`. Re-running the command pushes a new image tag and rolls the Cloud Run service forward. ## How the build runs The generated `cloudbuild.yaml` is a single Cloud Build step that: 1. Writes the Dockerfile into the build workspace via a single-quoted heredoc: ```yaml - | cat > Dockerfile <<'REFLEX_DOCKERFILE_EOF' FROM python:3.13-slim ... REFLEX_DOCKERFILE_EOF docker build -t "$_IMAGE" . docker push "$_IMAGE" ``` 2. Builds and pushes the image, tagging it with `_IMAGE` (passed to `gcloud builds submit` as `--substitutions=_IMAGE=...`). Because Cloud Build runs its own substitution pass over `args`, every literal `$` in the Dockerfile is doubled to `$$` before embedding (e.g. `ENV PATH="${UV_PROJECT_ENVIRONMENT}/bin:$PATH"` becomes `ENV PATH="$${UV_PROJECT_ENVIRONMENT}/bin:$$PATH"` in the YAML). Cloud Build's parser converts `$$` back to `$` before bash runs, so the Dockerfile written into the workspace contains the original characters. ## Security model The CLI runs the deploy script under a **restricted environment**. Only an explicit allowlist of host variables is forwarded to `bash` — things like `PATH`, `HOME`, `CLOUDSDK_*`, `DOCKER_*`, and proxy/TLS variables. Unrelated host secrets such as `AWS_*`, `GITHUB_TOKEN`, or arbitrary user variables are **not** forwarded, so a tampered or compromised manifest cannot exfiltrate them. You can preview the rewritten script, generated `cloudbuild.yaml`, and Dockerfile before anything runs by using `--dry-run`: ```bash reflex cloud deploy --gcp \ --gcp-project my-gcp-project-id \ --dry-run ``` ## Non-interactive use (CI) For automated pipelines, pass `--no-interactive` and an explicit `--token`: ```bash reflex cloud deploy --gcp \ --gcp-project "$GCP_PROJECT_ID" \ --service-name my-reflex-app \ --token "$REFLEX_TOKEN" \ --no-interactive ``` In non-interactive mode the CLI will not prompt, and it will exit non-zero if a token cannot be resolved. ## Troubleshooting **`Reflex denied the request (403). GCP Cloud Run deploys require an Enterprise tier subscription.`** Your account is not on the Enterprise tier. Contact [sales@reflex.dev](mailto:sales@reflex.dev). **`Billing must be enabled for activation of service(s) ...` (`UREQ_PROJECT_BILLING_NOT_FOUND`)** Attach a billing account to the GCP project, or use a different `--gcp-project`. **`The value of '--project' flag was set to Project number. To use this command, set it to PROJECT ID instead.`** Pass the project ID (e.g. `my-app-123456`), not the numeric project number. **`No active GCP account found.`** Run `gcloud auth login` and `gcloud auth application-default login`. **`The 'gcloud' / 'docker' / 'bash' CLI was not found on PATH.`** Install the missing tool and ensure it's on `PATH` for the shell you're invoking the CLI from. **`Dockerfile content contains the reserved heredoc marker 'REFLEX_DOCKERFILE_EOF'.`** Vanishingly unlikely — the Dockerfile from Reflex Cloud happens to contain a line that exactly matches the heredoc terminator the CLI uses to embed it. Re-run after the next CLI release, or open an issue. **`Couldn't find 'gcloud builds submit' in the deploy script.`** The CLI rewrites the `gcloud builds submit` block in the Reflex-supplied deploy script to use `--config=`. If Reflex Cloud changes the shape of that script before the CLI is updated to match, you'll see this error — upgrade `reflex-hosting-cli` (`uv tool upgrade reflex-hosting-cli` or `pip install -U reflex-hosting-cli`). # Deploy with Github Actions Source: http://localhost:3000/docs/hosting/deploy-with-github-actions.md ```python exec import reflex as rx code_style = { "color": rx.color("violet", 11), "border_radius": "0.25rem", "border": f"1px solid {rx.color('violet', 5)}", "background": rx.color("violet", 3), } cell_style = { "font_family": "Instrument Sans", "font_style": "normal", "font_weight": "500", "font_size": "14px", "line_height": "1.5", "letter_spacing": "-0.0125em", "color": "var(--secondary-11)", } github_actions_configs = [ { "name": "auth_token", "description": "Reflex authentication token stored in GitHub Secrets.", "required": True, "default": "N/A", }, { "name": "project_id", "description": "The ID of the project you want to deploy to.", "required": True, "default": "N/A", }, { "name": "app_directory", "description": "The directory containing your Reflex app.", "required": False, "default": ". (root)", }, { "name": "extra_args", "description": "Additional arguments to pass to the `reflex deploy` command.", "required": False, "default": "N/A", }, { "name": "python_version", "description": "The Python version to use for the deployment environment.", "required": False, "default": "3.12", }, ] ``` This GitHub Action simplifies the deployment of Reflex applications to Reflex Cloud. It handles setting up the environment, installing the Reflex CLI, and deploying your app with minimal configuration. ```md alert info # This action requires `reflex>=0.6.6` ``` **Features:** - Deploy Reflex apps directly from your GitHub repository to Reflex Cloud. - Supports subdirectory-based app structures. - Securely uses authentication tokens via GitHub Secrets. ## Usage ### Add the Action to Your Workflow Create a `.github/workflows/deploy.yml` file in your repository and add the following: ```yaml name: Deploy Reflex App on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Deploy to Reflex Cloud uses: reflex-dev/reflex-deploy-action@v1 with: auth_token: ${{ secrets.REFLEX_PROJECT_ID }} project_id: ${{ secrets.REFLEX_PROJECT_ID }} app_directory: "my-app-folder" # Optional, defaults to root extra_args: "--env THIRD_PARTY_APIKEY=***" # Optional python_version: "3.12" # Optional ``` ### Set Up Your Secrets Store your Reflex authentication token securely in your repository's secrets: 1. Go to your GitHub repository. 2. Navigate to Settings > Secrets and variables > Actions > New repository secret. 3. Create new secrets for `REFLEX_AUTH_TOKEN` and `REFLEX_PROJECT_ID`. (Create a `REFLEX_AUTH_TOKEN` in the tokens tab of your UI, check out these [docs](/docs/hosting/tokens/#tokens). The `REFLEX_PROJECT_ID` can be found in the UI when you click on the How to deploy button on the top right when inside a project and copy the ID after the `--project` flag.) ### Inputs ```python eval rx.table.root( rx.table.header( rx.table.row( rx.table.column_header_cell("Name"), rx.table.column_header_cell("Description"), rx.table.column_header_cell("required"), rx.table.column_header_cell("Default"), ), ), rx.table.body(*[ rx.table.row( rx.table.cell(rx.code(github_config["name"], style=code_style)), rx.table.cell(github_config["description"], style=cell_style), rx.table.cell(rx.code(github_config["required"], style=code_style)), rx.table.cell( rx.code(github_config["default"], style=code_style), min_width="100px" ), ) for github_config in github_actions_configs ]), variant="surface", ) ``` # Logs Source: http://localhost:3000/docs/hosting/logs.md ```python exec import reflex as rx ``` ## View Logs To view the app logs follow the arrow in the image below and press on the `Logs` dropdown. ```python eval rx.image( src="https://web.reflex-assets.dev/other/view_logs.webp", alt="Viewing app logs in Reflex Cloud", padding_bottom="20px", ) ``` ```md alert info # CLI Command to view logs `reflex cloud apps logs [OPTIONS] [APP_ID]` ``` ## View Deployment Logs and Deployment History To view the deployment history follow the arrow in the image below and press on the `Deployments`. ```python eval rx.image( src="https://web.reflex-assets.dev/other/view_deployment_logs.webp", alt="Viewing deployment logs in Reflex Cloud", ) ``` This brings you to the page below where you can see the deployment history of your app. Click on deployment you wish to explore further. ```python eval rx.image( src="https://web.reflex-assets.dev/other/view_deployment_logs_2.webp", alt="Deployment logs and history in Reflex Cloud", padding_bottom="20px", ) ``` ```md alert info # CLI Command to view deployment history `reflex cloud apps history [OPTIONS] [APP_ID]` ``` This brings you to the page below where you can view the deployment logs of your app by clicking the `Build logs` dropdown. ```python eval rx.image( src="https://web.reflex-assets.dev/other/view_deployment_logs_3.webp", alt="CLI command to view deployment history", ) ``` # Machine Types Source: http://localhost:3000/docs/hosting/machine-types.md ```python exec import reflex as rx ``` ## Machine Types To scale your app you can choose different VMTypes. VMTypes are different configurations of CPU and RAM. To scale your VM in the Cloud UI, click on the `Settings` tab in the Cloud UI on the app page, and then click on the `Scale` tab as shown below. Clicking on the `Change VM` button will allow you to scale your app. ```python eval rx.image( src="https://web.reflex-assets.dev/other/scaling_vms.webp", alt="Selecting machine types for scaling VMs", padding_bottom="20px", ) ``` ### VMTypes in the CLI To get all the possible VMTypes you can run the following command: ```bash reflex cloud vmtypes ``` To set which VMType to use when deploying your app you can pass the `--vmtype` flag with the id of the VMType. For example: ```bash reflex deploy --project f88b1574-f101-####-####-5f########## --vmtype c2m4 ``` This will deploy your app with the `c2m4` VMType, giving your app 2 CPU cores and 4 GB of RAM. # Regions Source: http://localhost:3000/docs/hosting/regions.md ```python exec import reflex as rx REGIONS_DICT = { "ams": "Amsterdam, Netherlands", "arn": "Stockholm, Sweden", "bom": "Mumbai, India", "cdg": "Paris, France", "dfw": "Dallas, Texas (US)", "ewr": "Secaucus, NJ (US)", "fra": "Frankfurt, Germany", "gru": "Sao Paulo, Brazil", "iad": "Ashburn, Virginia (US)", "jnb": "Johannesburg, South Africa", "lax": "Los Angeles, California (US)", "lhr": "London, United Kingdom", "nrt": "Tokyo, Japan", "ord": "Chicago, Illinois (US)", "sjc": "San Jose, California (US)", "sin": "Singapore, Singapore", "syd": "Sydney, Australia", "yyz": "Toronto, Canada", } COUNTRIES_CODES = { "ams": "NL", "arn": "SE", "bom": "IN", "cdg": "FR", "dfw": "US", "ewr": "US", "fra": "DE", "gru": "BR", "iad": "US", "jnb": "ZA", "lax": "US", "lhr": "GB", "nrt": "JP", "ord": "US", "sjc": "US", "sin": "SG", "syd": "AU", "yyz": "CA", } ``` ## Regions To scale your app you can choose different regions. Regions are different locations around the world where your app can be deployed. To scale your app to multiple regions in the Cloud UI, click on the `Settings` tab in the Cloud UI on the app page, and then click on the `Regions` tab as shown below. Clicking on the `Add new region` button will allow you to scale your app to multiple regions. ```python eval rx.image( src="https://web.reflex-assets.dev/other/scaling_regions.webp", alt="Selecting deployment regions for scaling", padding_bottom="20px", ) ``` The table below show all the regions that can be deployed in. ```python eval rx.el.table( rx.el.thead( rx.el.tr( rx.el.th( rx.el.div( "Region", ), class_name="px-6 py-3 text-left text-sm font-semibold text-secondary-12 text-nowrap", ), rx.el.th( rx.el.div( "Country", ), class_name="px-6 py-3 text-left text-sm font-semibold text-secondary-12 text-nowrap", ), ), class_name="bg-secondary-2", ), rx.el.tbody( *[ rx.el.tr( rx.el.td( rx.el.div( region, class_name="h-5 rounded-md border justify-start items-center inline-flex bg-secondary-1 text-xs font-medium shrink-0 px-1.5 w-fit text-secondary-12 border-secondary-6", ), class_name="px-6 py-3", ), rx.el.td( rx.el.div( rx.image( src=f"https://build.reflex.dev/flags/{COUNTRIES_CODES[region]}.svg", alt="Region country flag", class_name="rounded-[2px] mr-2 w-5 h-4", ), REGIONS_DICT[region], class_name="flex flex-row items-center gap-2", ), class_name="px-6 py-3 text-sm font-medium text-secondary-9", ), class_name="even:bg-secondary-2 odd:bg-secondary-1 hover:bg-secondary-3", ) for region in REGIONS_DICT.keys() ], class_name="divide-y divide-secondary-4", ), class_name="w-full table-fixed rounded-xl overflow-hidden divide-y divide-secondary-4", ) ``` ### Selecting Regions to Deploy in the CLI Below is an example of how to deploy your app in several regions: ```bash reflex deploy --project f88b1574-f101-####-####-5f########## --region sjc --region iad ``` By default all apps are deloyed in `sjc` if no other regions are given. If you wish to deploy in another region or several regions you can pass the `--region` flag (`-r` also works) with the region code. Check out all the regions that we can deploy to below: ## Config File To create a `config.yml` file for your app run the command below: ```bash reflex cloud config ``` This will create a yaml file similar to the one below where you can edit the app configuration: ```yaml name: medo description: '' regions: sjc: 1 lhr: 2 vmtype: c1m1 hostname: null envfile: .env project: null packages: - procps ``` # Secrets (Environment Variables) Source: http://localhost:3000/docs/hosting/secrets-environment-vars.md ```python exec import reflex as rx ``` ## Adding Secrets through the CLI Below is an example of how to use an environment variable file. You can pass the `--envfile` flag with the path to the env file. For example: ```bash reflex deploy --project f88b1574-f101-####-####-5f########## --envfile .env ``` In this example the path to the file is `.env`. If you prefer to pass the environment variables manually below is deployment command example: ```bash reflex deploy --project f88b1574-f101-####-####-5f########## --env OPENAI_API_KEY=sk-proj-vD4i9t6U############################ ``` They are passed after the `--env` flag as key value pairs. To pass multiple environment variables, you can repeat the `--env` tag. i.e. `reflex deploy --project f88b1574-f101-####-####-5f########## --env KEY1=VALUE1 --env KEY2=VALUE`. The `--envfile` flag will override any envs set manually. ```md alert info # More information on Environment Variables Environment variables are encrypted and safely stored. We recommend that backend API keys or secrets are entered as `envs`. Make sure to enter the `envs` without any quotation marks. We do not show the values of them in any CLI commands, only their names (or keys). You access the values of `envs` by referencing `os.environ` with their names as keys in your app's backend. For example, if you set an env `ASYNC_DB_URL`, you are able to access it by `os.environ["ASYNC_DB_URL"]`. Some Python libraries automatically look for certain environment variables. For example, `OPENAI_API_KEY` for the `openai` python client. The `boto3` client credentials can be configured by setting `AWS_ACCESS_KEY_ID`,`AWS_SECRET_ACCESS_KEY`. This information is typically available in the documentation of the Python packages you use. ``` ## Adding Secrets through the Cloud UI To find the secrets tab, click on the `Settings` tab in the Cloud UI on the app page. ```python eval rx.image( src="https://web.reflex-assets.dev/other/environment_variables.webp", alt="Environment variables panel in Reflex Cloud UI", ) ``` Then click on the `Secrets` tab as shown below. ```python eval rx.image( src="https://web.reflex-assets.dev/other/environment_variables_2.webp", alt="Adding environment variables in Reflex Cloud UI", ) ``` From here you can add or edit your environment variables. You will need to restart your app for these changes to take effect. This functionality in the UI can be disabled by an admin of the project. # Security Scan Source: http://localhost:3000/docs/hosting/security-scan.md ```python exec import reflex as rx ``` The `reflex cloud scan` command runs a Reflex-aware security review over your app source. It uploads your project to Reflex Cloud, checks it for security and logic flaws, and reports findings by severity. ```md alert info # CLI Command to scan an app `reflex cloud scan [OPTIONS] [DIRECTORY]` ``` A scan requires authentication. Run `reflex login` first if you are not already logged in. ## Running a Scan From the root of your app, run: ```bash reflex cloud scan ``` This scans the current directory. Pass a path to scan elsewhere: ```bash reflex cloud scan path/to/app ``` The command zips your app source, skipping dependency and build directories (`.web`, `node_modules`, `.venv`, `__pycache__`, and similar), submits it for review, and waits for the results. ## Reading the Results Findings are printed sorted by severity: - **CRITICAL**: fix immediately. - **HIGH**: serious; fix soon. - **MEDIUM**: should be addressed. - **LOW**: minor issues and recommendations. Each finding shows the rule that triggered, its category, the file and line, and a description. A recommended fix is included when available. If nothing is found, the command reports a clean review. ## Failing on Severity `--fail-on` makes the command exit non-zero when a finding at or above the given severity is present: ```bash reflex cloud scan --fail-on high ``` The default is `low`, so any finding causes a non-zero exit. Pass `--fail-on none` to always exit `0`. ## JSON Output `--json` (or `-j`) prints the raw result as JSON instead of formatted output: ```bash reflex cloud scan --json ``` ## Running in CI `--fail-on` sets the exit code, so a scan can block a merge or deploy when issues are found. Pass a token with `--token` and add `--no-interactive` so the command never prompts. Create a `REFLEX_AUTH_TOKEN` in the tokens tab of the Cloud UI (see the [tokens docs](/docs/hosting/tokens/#tokens)) and store it as a repository secret. This GitHub Actions workflow fails the build on any `high` or `critical` finding: ```yaml name: Security Scan on: pull_request: branches: - main jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install Reflex run: pip install reflex - name: Run security scan run: reflex cloud scan --no-interactive --fail-on high --token ${{ secrets.REFLEX_AUTH_TOKEN }} ``` ## Options See the [CLI reference](/docs/hosting/cli/scan/) for the full list of options. # Self Hosting Source: http://localhost:3000/docs/hosting/self-hosting.md ```python exec import reflex as rx ``` We recommend using `reflex deploy`, but if this does not fit your use case then you can also host your apps yourself. Clone your code to a server and install the [requirements](/docs/getting-started/installation/). ## API URL Edit your `rxconfig.py` file and set `api_url` to the publicly accessible IP address or hostname of your server, with the port `:8000` at the end. Setting this correctly is essential for the frontend to interact with the backend state. For example if your server is at `app.example.com`, your config would look like this: ```python config = rx.Config( app_name="your_app_name", api_url="http://app.example.com:8000", ) ``` It is also possible to set the environment variable `API_URL` at run time or export time to retain the default for local development. ## Proxying to a Subpath If you want to serve the backend behind a reverse proxy at a subpath (e.g. nginx routing `/api/*` to Reflex), set `backend_path` on the config instead of baking the prefix into `api_url`. Every backend endpoint (event websocket, `/ping`, `/_upload`, `/_health`, `/_all_routes`) is mounted under that prefix, and the frontend baked into the export automatically calls the backend at the prefixed URLs — no request rewriting in the proxy is required. ```python config = rx.Config( app_name="your_app_name", api_url="http://app.example.com:8000", backend_path="/api", ) ``` `frontend_path` plays the analogous role for the frontend and the two are independent. Note: changing `backend_path` (or `frontend_path`) requires a full restart of `reflex run` — routes and mount points are registered at startup, so hot reload alone will not move them. ## Production Mode Then run your app in production mode: ```bash reflex run --env prod ``` Production mode creates an optimized build of your app. By default, the static frontend of the app (HTML, Javascript, CSS) will be exposed on port `3000` and the backend (event handlers) will be listening on port `8000`. ```md alert warning # Reverse Proxy and Websockets Because the backend uses websockets, some reverse proxy servers, like [nginx](https://nginx.org/en/docs/http/websocket.html) or [apache](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#protoupgrade), must be configured to pass the `Upgrade` header to allow backend connectivity. ``` ## Exporting a Static Build Exporting a static build of the frontend allows the app to be served using a static hosting provider, like Netlify or Github Pages. Be sure `api_url` is set to an accessible backend URL when the frontend is exported. ```bash API_URL=http://app.example.com:8000 reflex export ``` This will create a `frontend.zip` file with your app's minified HTML, Javascript, and CSS build that can be uploaded to your static hosting service. It also creates a `backend.zip` file with your app's backend python code to upload to your server and run. You can export only the frontend or backend by passing in the `--frontend-only` or `--backend-only` flags. It is also possible to export the components without zipping. To do this, use the `--no-zip` parameter. This provides the frontend in the `.web/build/client/` directory and the backend can be found in the root directory of the project. ## Reflex Container Service Another option is to run your Reflex service in a container. For this purpose, a `Dockerfile` and additional documentation is available in the Reflex project in the directory `docker-example`. For the build of the container image it is necessary to edit the `rxconfig.py` and the add the `requirements.txt` to your project folder. The following changes are necessary in `rxconfig.py`: ```python config = rx.Config( app_name="app", api_url="http://app.example.com:8000", ) ``` Notice that the `api_url` should be set to the externally accessible hostname or IP, as the client browser must be able to connect to it directly to establish interactivity. You can find the `requirements.txt` in the `docker-example` folder of the project too. The project structure should looks like this: ```bash hello ├── .web ├── assets ├── hello │ ├── __init__.py │ └── hello.py ├── rxconfig.py ├── Dockerfile └── requirements.txt ``` After all changes have been made, the container image can now be created as follows. ```bash docker build -t reflex-project:latest . ``` Finally, you can start your Reflex container service as follows. ```bash docker run -d -p 3000:3000 -p 8000:8000 --name app reflex-project:latest ``` # Tokens Source: http://localhost:3000/docs/hosting/tokens.md ```python exec import reflex as rx ``` A token gives someone else all the permissions you have as a User or Admin. They can run any Reflex Cloud command from the CLI as if they were you, using the `--token` flag. A common use case is for GitHub Actions (you store this token in your secrets). To access or create tokens, first click the avatar in the top-right corner to open the drop-down menu, then click `Account Settings`. ```python eval rx.image( src="https://web.reflex-assets.dev/other/hosting_tokens_1.webp", alt="Adding tokens to Reflex Cloud", padding="1em 0em", ) ``` Clicking `Account Settings` will redirect you to both the `Settings` and `Tokens` dashboards. Click the `Tokens` tab at the top to access your tokens or create new ones. ```python eval rx.image( src="https://web.reflex-assets.dev/other/hosting_tokens_2.webp", alt="Adding tokens to Reflex Cloud", padding="1em 0em", ) ``` # Avatar Source: http://localhost:3000/docs/library/data-display/avatar.md --- components: - rx.avatar Avatar: | lambda **props: rx.hstack(rx.avatar(src="https://web.reflex-assets.dev/other/logo.jpg", **props), rx.avatar(fallback="RX", **props), spacing="3") --- ```python exec import reflex as rx ``` The Avatar component is used to represent a user, and display their profile pictures or fallback texts such as initials. ## Basic Example To create an avatar component with an image, pass the image URL as the `src` prop. ```python demo rx.avatar(src="https://web.reflex-assets.dev/other/logo.jpg") ``` To display a text such as initials, set the `fallback` prop without passing the `src` prop. ```python demo rx.avatar(fallback="RX") ``` ## Styling ### Size The `size` prop controls the size and spacing of the avatar. The acceptable size is from `"1"` to `"9"`, with `"3"` being the default. ```python demo rx.flex( rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="1" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="2" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="3" ), rx.avatar(src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX"), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="4" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="5" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="6" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="7" ), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", size="8" ), spacing="1", ) ``` ### Variant The `variant` prop controls the visual style of the avatar fallback text. The variant can be `"solid"` or `"soft"`. The default is `"soft"`. ```python demo rx.flex( rx.avatar(fallback="RX", variant="solid"), rx.avatar(fallback="RX", variant="soft"), rx.avatar(fallback="RX"), spacing="2", ) ``` ### Color Scheme The `color_scheme` prop sets a specific color to the fallback text, ignoring the global theme. ```python demo rx.flex( rx.avatar(fallback="RX", color_scheme="indigo"), rx.avatar(fallback="RX", color_scheme="cyan"), rx.avatar(fallback="RX", color_scheme="orange"), rx.avatar(fallback="RX", color_scheme="crimson"), spacing="2", ) ``` ### High Contrast The `high_contrast` prop increases color contrast of the fallback text with the background. ```python demo rx.grid( rx.avatar(fallback="RX", variant="solid"), rx.avatar(fallback="RX", variant="solid", high_contrast=True), rx.avatar(fallback="RX", variant="soft"), rx.avatar(fallback="RX", variant="soft", high_contrast=True), rows="2", spacing="2", flow="column", ) ``` ### Radius The `radius` prop sets specific radius value, ignoring the global theme. It can take values `"none" | "small" | "medium" | "large" | "full"`. ```python demo rx.grid( rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", radius="none" ), rx.avatar(fallback="RX", radius="none"), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", radius="small", ), rx.avatar(fallback="RX", radius="small"), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", radius="medium", ), rx.avatar(fallback="RX", radius="medium"), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", radius="large", ), rx.avatar(fallback="RX", radius="large"), rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RX", radius="full" ), rx.avatar(fallback="RX", radius="full"), rows="2", spacing="2", flow="column", ) ``` ### Fallback The `fallback` prop indicates the rendered text when the `src` cannot be loaded. ```python demo rx.flex( rx.avatar(fallback="RX"), rx.avatar(fallback="PC"), spacing="2", ) ``` ## Final Example As part of a user profile page, the Avatar component is used to display the user's profile picture, with the fallback text showing the user's initials. Text components displays the user's full name and username handle and a Button component shows the edit profile button. ```python demo rx.flex( rx.avatar( src="https://web.reflex-assets.dev/other/logo.jpg", fallback="RU", size="9" ), rx.text("Reflex User", weight="bold", size="4"), rx.text("@reflexuser", color_scheme="gray"), rx.button("Edit Profile", color_scheme="indigo", variant="solid"), direction="column", spacing="1", ) ``` ## API Reference ### rx.avatar An image element with a fallback for representing the user. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | Literal["solid", "soft"] | - | The variant of the avatar. | | `size` | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"], Breakpoints[str, Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]] | - | The size of the avatar: "1" - "9". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Color theme of the avatar. | | `high_contrast` | bool | - | Whether to render the avatar with higher contrast color against background. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | Override theme radius for avatar: "none", "small", "medium", "large", "full". | | `src` | str | - | The src of the avatar image. | | `fallback` | str | - | The rendered fallback text. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Badge Source: http://localhost:3000/docs/library/data-display/badge.md --- components: - rx.badge Badge: | lambda **props: rx.badge("Basic Badge", **props) --- ```python exec import reflex as rx ``` Badges are used to highlight an item's status for quick recognition. ## Basic Example To create a badge component with only text inside, pass the text as an argument. ```python demo rx.badge("New") ``` ## Styling ### Size The `size` prop controls the size and padding of a badge. It can take values of `"1" | "2"`, with default being `"1"`. ```python demo rx.flex( rx.badge("New"), rx.badge("New", size="1"), rx.badge("New", size="2"), align="center", spacing="2", ) ``` ### Variant The `variant` prop controls the visual style of the badge. The supported variant types are `"solid" | "soft" | "surface" | "outline"`. The variant default is `"soft"`. ```python demo rx.flex( rx.badge("New", variant="solid"), rx.badge("New", variant="soft"), rx.badge("New"), rx.badge("New", variant="surface"), rx.badge("New", variant="outline"), spacing="2", ) ``` ### Color Scheme The `color_scheme` prop sets a specific color, ignoring the global theme. ```python demo rx.flex( rx.badge("New", color_scheme="indigo"), rx.badge("New", color_scheme="cyan"), rx.badge("New", color_scheme="orange"), rx.badge("New", color_scheme="crimson"), spacing="2", ) ``` ### High Contrast The `high_contrast` prop increases color contrast of the fallback text with the background. ```python demo rx.flex( rx.flex( rx.badge("New", variant="solid"), rx.badge("New", variant="soft"), rx.badge("New", variant="surface"), rx.badge("New", variant="outline"), spacing="2", ), rx.flex( rx.badge("New", variant="solid", high_contrast=True), rx.badge("New", variant="soft", high_contrast=True), rx.badge("New", variant="surface", high_contrast=True), rx.badge("New", variant="outline", high_contrast=True), spacing="2", ), direction="column", spacing="2", ) ``` ### Radius The `radius` prop sets specific radius value, ignoring the global theme. It can take values `"none" | "small" | "medium" | "large" | "full"`. ```python demo rx.flex( rx.badge("New", radius="none"), rx.badge("New", radius="small"), rx.badge("New", radius="medium"), rx.badge("New", radius="large"), rx.badge("New", radius="full"), spacing="3", ) ``` ## Final Example A badge may contain more complex elements within it. This example uses a `flex` component to align an icon and the text correctly, using the `gap` prop to ensure a comfortable spacing between the two. ```python demo rx.badge( rx.flex( rx.icon(tag="arrow_up"), rx.text("8.8%"), spacing="1", ), color_scheme="grass", ) ``` ## API Reference ### rx.badge A stylized badge element. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `variant` | Literal["solid", "soft", "surface", "outline"] | - | The variant of the badge. | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the badge. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Color theme of the badge. | | `high_contrast` | bool | - | Whether to render the badge with higher contrast color against background. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | Override theme radius for badge: "none", "small", "medium", "large", "full". | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Callout Source: http://localhost:3000/docs/library/data-display/callout.md --- components: - rx.callout Callout: | lambda **props: rx.callout("Basic Callout", icon="search", **props) CalloutRoot: | lambda **props: rx.callout.root( rx.callout.icon(rx.icon(tag="info")), rx.callout.text("You will need admin privileges to install and access this application."), **props ) --- ```python exec import reflex as rx ``` A `callout` is a short message to attract user's attention. ```python demo rx.callout( "You will need admin privileges to install and access this application.", icon="info", ) ``` The `icon` prop allows an icon to be passed to the `callout` component. See the [**icon** component for all icons that are available.](/docs/library/data-display/icon) ## As alert ```python demo rx.callout( "Access denied. Please contact the network administrator to view this page.", icon="triangle_alert", color_scheme="red", role="alert", ) ``` ## Style ### Size Use the `size` prop to control the size. ```python demo rx.flex( rx.callout( "You will need admin privileges to install and access this application.", icon="info", size="3", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", size="2", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", size="1", ), direction="column", spacing="3", align="start", ) ``` ### Variant Use the `variant` prop to control the visual style. It is set to `soft` by default. ```python demo rx.flex( rx.callout( "You will need admin privileges to install and access this application.", icon="info", variant="soft", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", variant="surface", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", variant="outline", ), direction="column", spacing="3", ) ``` ### Color Use the `color_scheme` prop to assign a specific color, ignoring the global theme. ```python demo rx.flex( rx.callout( "You will need admin privileges to install and access this application.", icon="info", color_scheme="blue", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", color_scheme="green", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", color_scheme="red", ), direction="column", spacing="3", ) ``` ### High Contrast Use the `high_contrast` prop to add additional contrast. ```python demo rx.flex( rx.callout( "You will need admin privileges to install and access this application.", icon="info", ), rx.callout( "You will need admin privileges to install and access this application.", icon="info", high_contrast=True, ), direction="column", spacing="3", ) ``` ## API Reference ### rx.callout A short message to attract user's attention. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `as_child` | bool | - | Change the default rendered element for the one passed as a child, merging their props and behavior. | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | Size "1" - "3". | | `variant` | Literal["soft", "surface", "outline"] | - | Variant of button: "soft", "surface", "outline". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Override theme color for button. | | `high_contrast` | bool | - | Whether to render the button with higher contrast color against background. | | `text` | str | - | The text of the callout. | | `icon` | str | - | The icon of the callout. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Code Block Source: http://localhost:3000/docs/library/data-display/code-block.md --- components: - rx.code_block --- ```python exec import reflex as rx ``` The Code Block component can be used to display code easily within a website. Put in a multiline string with the correct spacing and specify and language to show the desired code. ```python demo rx.code_block( """def fib(n): if n <= 1: return n else: return(fib(n-1) + fib(n-2))""", language="python", show_line_numbers=True, ) ``` ## Themes The `theme` prop must be set to a `Theme` value accessed from the `rx.code_block.themes` namespace; strings are not accepted. By default, the code block uses `one_light` in light mode and `one_dark` in dark mode. ```python demo rx.code_block( """print("Hello, world!")""", language="python", theme=rx.code_block.themes.dracula, ) ``` To pick a theme that responds to the global color mode, pass `rx.color_mode_cond` with the desired light and dark variants: ```python demo rx.code_block( """print("Hello, world!")""", language="python", theme=rx.color_mode_cond( light=rx.code_block.themes.one_light, dark=rx.code_block.themes.one_dark, ), ) ``` ## API Reference ### rx.code_block A code block. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `theme` | Theme | - | The theme to use, accessed via rx.code_block.themes (e.g. rx.code_block.themes.one_dark). | | `language` | Literal["abap", "abnf", "actionscript", "ada", "agda", "al", "antlr4", "apacheconf", "apex", "apl", "applescript", "aql", "arduino", "arff", "asciidoc", "asm6502", "asmatmel", "aspnet", "autohotkey", "autoit", "avisynth", "avro-idl", "bash", "basic", "batch", "bbcode", "bicep", "birb", "bison", "bnf", "brainfuck", "brightscript", "bro", "bsl", "c", "cfscript", "chaiscript", "cil", "clike", "clojure", "cmake", "cobol", "coffeescript", "concurnas", "coq", "core", "cpp", "crystal", "csharp", "cshtml", "csp", "css", "css-extras", "csv", "cypher", "d", "dart", "dataweave", "dax", "dhall", "diff", "django", "dns-zone-file", "docker", "dot", "ebnf", "editorconfig", "eiffel", "ejs", "elixir", "elm", "erb", "erlang", "etlua", "excel-formula", "factor", "false", "firestore-security-rules", "flow", "fortran", "fsharp", "ftl", "gap", "gcode", "gdscript", "gedcom", "gherkin", "git", "glsl", "gml", "gn", "go", "go-module", "graphql", "groovy", "haml", "handlebars", "haskell", "haxe", "hcl", "hlsl", "hoon", "hpkp", "hsts", "http", "ichigojam", "icon", "icu-message-format", "idris", "iecst", "ignore", "index", "inform7", "ini", "io", "j", "java", "javadoc", "javadoclike", "javascript", "javastacktrace", "jexl", "jolie", "jq", "js-extras", "js-templates", "jsdoc", "json", "json5", "jsonp", "jsstacktrace", "jsx", "julia", "keepalived", "keyman", "kotlin", "kumir", "kusto", "latex", "latte", "less", "lilypond", "liquid", "lisp", "livescript", "llvm", "log", "lolcode", "lua", "magma", "makefile", "markdown", "markup", "markup-templating", "matlab", "maxscript", "mel", "mermaid", "mizar", "mongodb", "monkey", "moonscript", "n1ql", "n4js", "nand2tetris-hdl", "naniscript", "nasm", "neon", "nevod", "nginx", "nim", "nix", "nsis", "objectivec", "ocaml", "opencl", "openqasm", "oz", "parigp", "parser", "pascal", "pascaligo", "pcaxis", "peoplecode", "perl", "php", "php-extras", "phpdoc", "plsql", "powerquery", "powershell", "processing", "prolog", "promql", "properties", "protobuf", "psl", "pug", "puppet", "pure", "purebasic", "purescript", "python", "q", "qml", "qore", "qsharp", "r", "racket", "reason", "regex", "rego", "renpy", "rest", "rip", "roboconf", "robotframework", "ruby", "rust", "sas", "sass", "scala", "scheme", "scss", "shell-session", "smali", "smalltalk", "smarty", "sml", "solidity", "solution-file", "soy", "sparql", "splunk-spl", "sqf", "sql", "squirrel", "stan", "stylus", "swift", "systemd", "t4-cs", "t4-templating", "t4-vb", "tap", "tcl", "textile", "toml", "tremor", "tsx", "tt2", "turtle", "twig", "typescript", "typoscript", "unrealscript", "uorazor", "uri", "v", "vala", "vbnet", "velocity", "verilog", "vhdl", "vim", "visual-basic", "warpscript", "wasm", "web-idl", "wiki", "wolfram", "wren", "xeora", "xml-doc", "xojo", "xquery", "yaml", "yang", "zig"] | - | The language to use. | | `code` | str | - | The code to display. | | `show_line_numbers` | bool | - | If this is enabled line numbers will be shown next to the code block. | | `starting_line_number` | int | - | The starting line number to use. | | `wrap_long_lines` | bool | - | Whether to wrap long lines. | | `code_tag_props` | dict[str, str, dict[str, str]] | - | Props passed down to the code tag. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Data List Source: http://localhost:3000/docs/library/data-display/data-list.md --- components: - rx.data_list.root - rx.data_list.item - rx.data_list.label - rx.data_list.value DataListRoot: | lambda **props: rx.data_list.root( rx.foreach( [["Status", "Authorized"], ["ID", "U-474747"], ["Name", "Developer Success"], ["Email", "foo@reflex.dev"]], lambda item: rx.data_list.item(rx.data_list.label(item[0]), rx.data_list.value(item[1])), ), **props, ) DataListItem: | lambda **props: rx.data_list.root( rx.data_list.item( rx.data_list.label("Status"), rx.data_list.value(rx.badge("Authorized", variant="soft", radius="full", size="2")), **props, ), rx.data_list.item( rx.data_list.label("Name"), rx.data_list.value(rx.heading("Developer Success", as_="h2", size="4")), **props, ), rx.data_list.item( rx.data_list.label("Email"), rx.data_list.value(rx.link("foo@reflex.dev", href="mailto:foo@reflex.dev")), **props, ), ) DataListLabel: | lambda **props: rx.data_list.root( rx.foreach( [["Status", "Authorized"], ["ID", "U-474747"], ["Name", "Developer Success"], ["Email", "foo@reflex.dev"]], lambda item: rx.data_list.item(rx.data_list.label(item[0], **props), rx.data_list.value(item[1])), ), ) DataListValue: | lambda **props: rx.data_list.root( rx.foreach( [["Status", "Authorized"], ["ID", "U-474747"], ["Name", "Developer Success"], ["Email", "foo@reflex.dev"]], lambda item: rx.data_list.item(rx.data_list.label(item[0]), rx.data_list.value(item[1], **props)), ), ) --- ```python exec import reflex as rx ``` The `DataList` component displays key-value pairs and is particularly helpful for showing metadata. A `DataList` needs to be initialized using `rx.data_list.root()` and currently takes in data list items: `rx.data_list.item` ```python demo ( rx.card( rx.data_list.root( rx.data_list.item( rx.data_list.label("Status"), rx.data_list.value( rx.badge( "Authorized", variant="soft", radius="full", ) ), align="center", ), rx.data_list.item( rx.data_list.label("ID"), rx.data_list.value(rx.code("U-474747")), ), rx.data_list.item( rx.data_list.label("Name"), rx.data_list.value("Developer Success"), align="center", ), rx.data_list.item( rx.data_list.label("Email"), rx.data_list.value( rx.link( "success@reflex.dev", href="mailto:success@reflex.dev", ), ), ), rx.data_list.item( rx.data_list.label("Company"), rx.data_list.value( rx.link( "Reflex", href="https://reflex.dev", ), ), ), ), ), ) ``` ## API Reference ### rx.data_list.root Root element for a DataList component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `orientation` | Literal["horizontal", "vertical"], Breakpoints[str, Literal["horizontal", "vertical"]] | - | The orientation of the data list item: "horizontal", "vertical". | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the data list item: "1", "2", "3". | | `trim` | Literal["normal", "start", "end", "both"], Breakpoints[str, Literal["normal", "start", "end", "both"]] | - | Trims the leading whitespace from the start or end of the text. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.data_list.item An item in the DataList component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `align` | Literal["start", "center", "end", "baseline", "stretch"], Breakpoints[str, Literal["start", "center", "end", "baseline", "stretch"]] | - | The alignment of the data list item within its container. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.data_list.label A label in the DataList component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `width` | str, Breakpoints[str, str] | - | The width of the component. | | `min_width` | str, Breakpoints[str, str] | - | The minimum width of the component. | | `max_width` | str, Breakpoints[str, str] | - | The maximum width of the component. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color scheme for the DataList component. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.data_list.value A value in the DataList component. #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Icon Source: http://localhost:3000/docs/library/data-display/icon.md --- components: - rx.lucide.Icon --- ```python exec import reflex as rx from reflex_components_lucide.icon import LUCIDE_ICON_LIST from reflex.experimental.client_state import ClientStateVar icon_search_cs = ClientStateVar.create("icon_search", default="") @rx.memo def lucide_icons() -> rx.Component: return rx.box( rx.box( rx.box( rx.icon( tag="search", size=20, class_name="shrink-0 !text-secondary-11", ), class_name="absolute left-2 top-1/2 transform -translate-y-1/2 z-[1] shrink-0 flex items-center justify-center pb-2", ), rx.el.input( placeholder="Search icons...", on_change=icon_search_cs.set_value, class_name="relative box-border border-secondary-4 focus:border-violet-9 focus:border-1 bg-secondary-2 p-[0.5rem_0.75rem] border rounded-xl font-base text-secondary-11 placeholder:text-secondary-9 outline-none focus:outline-none w-full mb-2 pl-10", ), class_name="relative flex items-center", ), rx.el.div( rx.box( rx.foreach( LUCIDE_ICON_LIST, lambda icon: rx.cond( icon.startswith(icon_search_cs.value), rx.tooltip( rx.box( rx.icon(tag=icon), class_name="flex items-center justify-center rounded-md hover:bg-secondary-3 transition-bg p-2 cursor-pointer", on_click=[ rx.set_clipboard(icon), rx.toast.success(f"Copied {icon} to clipboard"), ], key=icon, ), content=icon, ), None, ), ), class_name="flex flex-wrap justify-start gap-4 mb-4", ), class_name="overflow-y-auto max-h-[500px]", ), class_name="flex flex-col gap-4", ) ``` The Icon component is used to display an icon from a library of icons. This implementation is based on the [Lucide Icons](https://lucide.dev/icons) where you can find a list of all available icons. ## Icons List ```python eval lucide_icons() ``` ## Basic Example To display an icon, specify the `tag` prop from the list of available icons. Passing the tag as the first children is also supported and will be assigned to the `tag` prop. The `tag` is expected to be in `snake_case` format, but `kebab-case` is also supported to allow copy-paste from [https://lucide.dev/icons](https://lucide.dev/icons). ```python demo rx.flex( rx.icon("calendar"), rx.icon(tag="calendar"), gap="2", ) ``` ## Dynamic Icons There are two ways to use dynamic icons in Reflex: ### Using rx.match If you have a specific subset of icons you want to use dynamically, you can define an `rx.match` with them: ```python def dynamic_icon_with_match(icon_name): return rx.match( icon_name, ("plus", rx.icon("plus")), ("minus", rx.icon("minus")), ("equal", rx.icon("equal")), ) ``` ```python exec def dynamic_icon_with_match(icon_name): return rx.match( icon_name, ("plus", rx.icon("plus")), ("minus", rx.icon("minus")), ("equal", rx.icon("equal")), ) ``` ### Using Dynamic Icon Tags Reflex also supports using dynamic values directly as the `tag` prop in `rx.icon()`. This allows you to use any icon from the Lucide library dynamically at runtime. ```python exec class DynamicIconState(rx.State): current_icon: str = "heart" def change_icon(self): icons = ["heart", "star", "bell", "calendar", "settings"] import random self.current_icon = random.choice(icons) ``` ```python demo rx.vstack( rx.heading("Dynamic Icon Example", as_="h2"), rx.icon(DynamicIconState.current_icon, size=30, color="red"), rx.button("Change Icon", on_click=DynamicIconState.change_icon), spacing="4", align="center", ) ``` Under the hood, when a dynamic value is passed as the `tag` prop to `rx.icon()`, Reflex automatically uses a special `DynamicIcon` component that can load icons at runtime. ```md alert When using dynamic icons, make sure the icon names are valid. Invalid icon names will cause runtime errors. ``` ## Styling Icon from Lucide can be customized with the following props `stroke_width`, `size` and `color`. ### Stroke Width ```python demo rx.flex( rx.icon("moon", stroke_width=1), rx.icon("moon", stroke_width=1.5), rx.icon("moon", stroke_width=2), rx.icon("moon", stroke_width=2.5), gap="2", ) ``` ### Size ```python demo rx.flex( rx.icon("zoom_in", size=15), rx.icon("zoom_in", size=20), rx.icon("zoom_in", size=25), rx.icon("zoom_in", size=30), align="center", gap="2", ) ``` ### Color Here is an example using basic colors in icons. ```python demo rx.flex( rx.icon("zoom_in", size=18, color="indigo"), rx.icon("zoom_in", size=18, color="cyan"), rx.icon("zoom_in", size=18, color="orange"), rx.icon("zoom_in", size=18, color="crimson"), gap="2", ) ``` A radix color with a scale may also be specified using `rx.color()` as seen below. ```python demo rx.flex( rx.icon("zoom_in", size=18, color=rx.color("purple", 1)), rx.icon("zoom_in", size=18, color=rx.color("purple", 2)), rx.icon("zoom_in", size=18, color=rx.color("purple", 3)), rx.icon("zoom_in", size=18, color=rx.color("purple", 4)), rx.icon("zoom_in", size=18, color=rx.color("purple", 5)), rx.icon("zoom_in", size=18, color=rx.color("purple", 6)), rx.icon("zoom_in", size=18, color=rx.color("purple", 7)), rx.icon("zoom_in", size=18, color=rx.color("purple", 8)), rx.icon("zoom_in", size=18, color=rx.color("purple", 9)), rx.icon("zoom_in", size=18, color=rx.color("purple", 10)), rx.icon("zoom_in", size=18, color=rx.color("purple", 11)), rx.icon("zoom_in", size=18, color=rx.color("purple", 12)), gap="2", ) ``` Here is another example using the `accent` color with scales. The `accent` is the most dominant color in your theme. ```python demo rx.flex( rx.icon("zoom_in", size=18, color=rx.color("accent", 1)), rx.icon("zoom_in", size=18, color=rx.color("accent", 2)), rx.icon("zoom_in", size=18, color=rx.color("accent", 3)), rx.icon("zoom_in", size=18, color=rx.color("accent", 4)), rx.icon("zoom_in", size=18, color=rx.color("accent", 5)), rx.icon("zoom_in", size=18, color=rx.color("accent", 6)), rx.icon("zoom_in", size=18, color=rx.color("accent", 7)), rx.icon("zoom_in", size=18, color=rx.color("accent", 8)), rx.icon("zoom_in", size=18, color=rx.color("accent", 9)), rx.icon("zoom_in", size=18, color=rx.color("accent", 10)), rx.icon("zoom_in", size=18, color=rx.color("accent", 11)), rx.icon("zoom_in", size=18, color=rx.color("accent", 12)), gap="2", ) ``` ## Final Example Icons can be used as child components of many other components. For example, adding a magnifying glass icon to a search bar. ```python demo rx.badge( rx.flex( rx.icon("search", size=18), rx.text("Search documentation...", size="3", weight="medium"), direction="row", gap="1", align="center", ), size="2", radius="full", color_scheme="gray", ) ``` ## API Reference ### rx.lucide.Icon An Icon component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | int | - | The size of the icon in pixels. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # List Source: http://localhost:3000/docs/library/data-display/list.md --- components: - rx.list.item - rx.list.ordered - rx.list.unordered --- ```python exec import reflex as rx ``` A `list` is a component that is used to display a list of items, stacked vertically by default. A `list` can be either `ordered` or `unordered`. It is based on the `flex` component and therefore inherits all of its props. `list.unordered` has bullet points to display the list items. ```python demo rx.list.unordered( rx.list.item("Example 1"), rx.list.item("Example 2"), rx.list.item("Example 3"), ) ``` `list.ordered` has numbers to display the list items. ```python demo rx.list.ordered( rx.list.item("Example 1"), rx.list.item("Example 2"), rx.list.item("Example 3"), ) ``` `list.unordered()` and `list.ordered()` can have no bullet points or numbers by setting the `list_style_type` prop to `none`. This is effectively the same as using the `list()` component. ```python demo rx.hstack( rx.list( rx.list.item("Example 1"), rx.list.item("Example 2"), rx.list.item("Example 3"), ), rx.list.unordered( rx.list.item("Example 1"), rx.list.item("Example 2"), rx.list.item("Example 3"), list_style_type="none", ), ) ``` Lists can also be used with icons. ```python demo rx.list( rx.list.item( rx.icon("circle_check_big", color="green"), " Allowed", ), rx.list.item( rx.icon("octagon_x", color="red"), " Not", ), rx.list.item(rx.icon("settings", color="grey"), " Settings"), list_style_type="none", ) ``` ## API Reference ### rx.list.item Display an item of an ordered or unordered list. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.list.ordered Display an ordered list. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `reversed` | bool | - | Reverses the order of the list. | | `start` | int | - | Specifies the start value of the first list item in an ordered list. | | `type` | Literal["1", "a", "A", "i", "I"] | - | Specifies the kind of marker to use in the list (letters or numbers). | | `list_style_type` | Literal["none", "disc", "circle", "square"], Literal["none", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", "georgian", "lower-alpha", "upper-alpha", "hiragana", "katakana"] | - | The style of the list. Default to "none". | | `items` | Iterable | - | A list of items to add to the list. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.list.unordered Display an unordered list. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `list_style_type` | Literal["none", "disc", "circle", "square"], Literal["none", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", "georgian", "lower-alpha", "upper-alpha", "hiragana", "katakana"] | - | The style of the list. Default to "none". | | `items` | Iterable | - | A list of items to add to the list. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Moment Source: http://localhost:3000/docs/library/data-display/moment.md --- components: - rx.moment --- Displaying date and relative time to now sometimes can be more complicated than necessary. To make it easy, Reflex is wrapping [react-moment](https://www.npmjs.com/package/react-moment) under `rx.moment`. ```python exec import reflex as rx from reflex.utils.serializers import serialize_datetime ``` ## Examples Using a date from a state var as a value, we will display it in a few different way using `rx.moment`. The `date_now` state var is initialized when the site was deployed. The button below can be used to update the var to the current datetime, which will be reflected in the subsequent examples. ```python demo exec from datetime import datetime, timezone class MomentState(rx.State): date_now: datetime = datetime.now(timezone.utc) @rx.event def update(self): self.date_now = datetime.now(timezone.utc) def moment_update_example(): return rx.button( "Update", rx.moment(MomentState.date_now), on_click=MomentState.update ) ``` ### Display the date as-is: ```python demo rx.moment(MomentState.date_now) ``` ### Humanized interval Sometimes we don't want to display just a raw date, but we want something more instinctive to read. That's when we can use `from_now` and `to_now`. ```python demo rx.moment(MomentState.date_now, from_now=True) ``` ```python demo rx.moment(MomentState.date_now, to_now=True) ``` You can also set a duration (in milliseconds) with `from_now_during` where the date will display as relative, then after that, it will be displayed as defined in `format`. ```python demo rx.moment( MomentState.date_now, from_now_during=100000 ) # after 100 seconds, date will display normally ``` ### Formatting dates ```python demo rx.moment(MomentState.date_now, format="YYYY-MM-DD") ``` ```python demo rx.moment(MomentState.date_now, format="HH:mm:ss") ``` ### Offset Date With the props `add` and `subtract`, you can pass an `rx.MomentDelta` object to modify the displayed date without affecting the stored date in your state. #### Add ```python demo rx.vstack( rx.moment( MomentState.date_now, add=rx.MomentDelta(years=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(quarters=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(months=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(weeks=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(days=2), format="YYYY-MM-DD - HH:mm:ss" ), rx.moment( MomentState.date_now, add=rx.MomentDelta(hours=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(minutes=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, add=rx.MomentDelta(seconds=2), format="YYYY-MM-DD - HH:mm:ss", ), ) ``` #### Subtract ```python demo rx.vstack( rx.moment( MomentState.date_now, subtract=rx.MomentDelta(years=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(quarters=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(months=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(weeks=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(days=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(hours=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(minutes=2), format="YYYY-MM-DD - HH:mm:ss", ), rx.moment( MomentState.date_now, subtract=rx.MomentDelta(seconds=2), format="YYYY-MM-DD - HH:mm:ss", ), ) ``` ### Timezones You can also set dates to display in a specific timezone: ```python demo rx.vstack( rx.moment(MomentState.date_now, tz="America/Los_Angeles"), rx.moment(MomentState.date_now, tz="Europe/Paris"), rx.moment(MomentState.date_now, tz="Asia/Tokyo"), ) ``` ### Client-side periodic update If a date is not passed to `rx.moment`, it will use the client's current time. If you want to update the date every second, you can use the `interval` prop. ```python demo rx.moment(interval=1000, format="HH:mm:ss") ``` Even better, you can actually link an event handler to the `on_change` prop that will be called every time the date is updated: ```python demo exec class MomentLiveState(rx.State): updating: bool = False @rx.event def on_update(self, date): return rx.toast(f"Date updated: {date}") @rx.event def set_updating(self, value: bool): self.updating = value def moment_live_example(): return rx.hstack( rx.moment( format="HH:mm:ss", interval=rx.cond(MomentLiveState.updating, 5000, 0), on_change=MomentLiveState.on_update, ), rx.switch( is_checked=MomentLiveState.updating, on_change=MomentLiveState.set_updating, ), ) ``` ## API Reference ### rx.moment The Moment component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `interval` | int | - | How often the date update (how often time update / 0 to disable). | | `format` | str | - | Formats the date according to the given format string. | | `trim` | bool | - | When formatting duration time, the largest-magnitude tokens are automatically trimmed when they have no value. | | `parse` | str | - | Use the parse attribute to tell moment how to parse the given date when non-standard. | | `add` | MomentDelta | - | Add a delta to the base date (keys are "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds"). | | `subtract` | MomentDelta | - | Subtract a delta to the base date (keys are "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds"). | | `from_now` | bool | - | Displays the date as the time from now, e.g. "5 minutes ago". | | `from_now_short` | bool | - | Displays the relative time in a short format using abbreviated units (e.g., "1h", "2d", "3mo", "1y" instead of "1 hour ago", "2 days ago", etc.). | | `from_now_during` | int | - | Setting fromNowDuring will display the relative time as with fromNow but just during its value in milliseconds, after that format will be used instead. | | `to_now` | bool | - | Similar to fromNow, but gives the opposite interval. | | `with_title` | bool | - | Adds a title attribute to the element with the complete date. | | `title_format` | str | - | How the title date is formatted when using the withTitle attribute. | | `diff` | str | - | Show the different between this date and the rendered child. | | `decimal` | bool | - | Display the diff as decimal. | | `unit` | str | - | Display the diff in given unit. | | `duration` | str | - | Shows the duration (elapsed time) between two dates. duration property should be behind date property time-wise. | | `date` | str, datetime, date, time, timedelta | - | The date to display (also work if passed as children). | | `duration_from_now` | bool | - | Shows the duration (elapsed time) between now and the provided datetime. | | `unix` | bool | - | Tells Moment to parse the given date value as a unix timestamp. | | `local` | bool | - | Outputs the result in local time. | | `tz` | str | - | Display the date in the given timezone. | | `locale` | str | - | The locale to use when rendering. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_change` | Fires when the date changes. | # Progress Source: http://localhost:3000/docs/library/data-display/progress.md --- components: - rx.progress Progress: | lambda **props: rx.box(rx.progress(value=50, **props), width="20rem") --- Progress is used to display the progress status for a task that takes a long time or consists of several steps. ```python exec import reflex as rx ``` ## Basic Example `rx.progress` expects the `value` prop to set the progress value. `width` is default to 100%, the width of its parent component. ```python demo rx.vstack( rx.progress(value=0), rx.progress(value=50), rx.progress(value=100), width="50%", ) ``` For a dynamic progress, you can assign a state variable to the `value` prop instead of a constant value. ```python demo exec import asyncio class ProgressState(rx.State): value: int = 0 @rx.event(background=True) async def start_progress(self): async with self: self.value = 0 while self.value < 100: await asyncio.sleep(0.1) async with self: self.value += 1 def live_progress(): return rx.hstack( rx.progress(value=ProgressState.value), rx.button("Start", on_click=ProgressState.start_progress), width="50%", ) ``` ## API Reference ### rx.progress A progress bar component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | int | - | The value of the progress bar: 0 to max (default 100). | | `max` | int | - | The maximum progress value. | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the progress bar: "1", "2", "3". | | `variant` | Literal["classic", "surface", "soft"] | - | The variant of the progress bar: "classic", "surface", "soft". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color theme of the progress bar. | | `high_contrast` | bool | - | Whether to render the progress bar with higher contrast color against background. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | Override theme radius for progress bar: "none", "small", "medium", "large", "full". | | `duration` | str | - | The duration of the progress bar animation. Once the duration times out, the progress bar will start an indeterminate animation. | | `fill_color` | str | - | The color of the progress bar fill animation. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Scroll Area Source: http://localhost:3000/docs/library/data-display/scroll-area.md --- components: - rx.scroll_area ScrollArea: | lambda **props: rx.scroll_area( rx.flex( rx.text( """Three fundamental aspects of typography are legibility, readability, and aesthetics. Although in a non-technical sense "legible" and "readable"are often used synonymously, typographically they are separate but related concepts.""", size="5", ), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="5", ), direction="column", spacing="4", height="100px", width="50%", ), **props ) --- ```python exec import random import reflex as rx ``` Custom styled, cross-browser scrollable area using native functionality. ## Basic Example ```python demo rx.scroll_area( rx.flex( rx.text( """Three fundamental aspects of typography are legibility, readability, and aesthetics. Although in a non-technical sense “legible” and “readable” are often used synonymously, typographically they are separate but related concepts.""", ), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as “the quality of being decipherable and recognisable”. For instance, if a “b” and an “h”, or a “3” and an “8”, are difficult to distinguish at small sizes, this is a problem of legibility.""", ), rx.text( """Typographers are concerned with legibility insofar as it is their job to select the correct font to use. Brush Script is an example of a font containing many characters that might be difficult to distinguish. The selection of cases influences the legibility of typography because using only uppercase letters (all-caps) reduces legibility.""", ), direction="column", spacing="4", ), type="always", scrollbars="vertical", style={"height": 180}, ) ``` ## Control the scrollable axes Use the `scrollbars` prop to limit scrollable axes. This prop can take values `"vertical" | "horizontal" | "both"`. ```python demo rx.grid( rx.scroll_area( rx.flex( rx.text( """Three fundamental aspects of typography are legibility, readability, and aesthetics. Although in a non-technical sense "legible" and "readable" are often used synonymously, typographically they are separate but related concepts.""", size="2", trim="both", ), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", padding_right="48px", direction="column", spacing="4", ), type="always", scrollbars="vertical", style={"height": 150}, ), rx.scroll_area( rx.flex( rx.text( """Three fundamental aspects of typography are legibility, readability, and aesthetics. Although in a non-technical sense "legible" and "readable" are often used synonymously, typographically they are separate but related concepts.""", size="2", trim="both", ), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", spacing="4", style={"width": 700}, ), type="always", scrollbars="horizontal", style={"height": 150}, ), rx.scroll_area( rx.flex( rx.text( """Three fundamental aspects of typography are legibility, readability, and aesthetics. Although in a non-technical sense "legible" and "readable" are often used synonymously, typographically they are separate but related concepts.""", size="2", trim="both", ), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", spacing="4", style={"width": 400}, ), type="always", scrollbars="both", style={"height": 150}, ), columns="3", spacing="2", ) ``` ## Setting the type of the Scrollbars The `type` prop describes the nature of scrollbar visibility. `auto` means that scrollbars are visible when content is overflowing on the corresponding orientation. `always` means that scrollbars are always visible regardless of whether the content is overflowing. `scroll` means that scrollbars are visible when the user is scrolling along its corresponding orientation. `hover` when the user is scrolling along its corresponding orientation and when the user is hovering over the scroll area. ```python demo rx.grid( rx.scroll_area( rx.flex( rx.text("type = 'auto'", weight="bold"), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", direction="column", spacing="4", ), type="auto", scrollbars="vertical", style={"height": 150}, ), rx.scroll_area( rx.flex( rx.text("type = 'always'", weight="bold"), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", direction="column", spacing="4", ), type="always", scrollbars="vertical", style={"height": 150}, ), rx.scroll_area( rx.flex( rx.text("type = 'scroll'", weight="bold"), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", direction="column", spacing="4", ), type="scroll", scrollbars="vertical", style={"height": 150}, ), rx.scroll_area( rx.flex( rx.text("type = 'hover'", weight="bold"), rx.text( """Legibility describes how easily individual characters can be distinguished from one another. It is described by Walter Tracy as "the quality of being decipherable and recognisable". For instance, if a "b" and an "h", or a "3" and an "8", are difficult to distinguish at small sizes, this is a problem of legibility.""", size="2", trim="both", ), padding="8px", direction="column", spacing="4", ), type="hover", scrollbars="vertical", style={"height": 150}, ), columns="4", spacing="2", ) ``` ## API Reference ### rx.scroll_area Custom styled, cross-browser scrollable area using native functionality. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `scrollbars` | Literal["vertical", "horizontal", "both"] | - | The alignment of the scroll area. | | `type` | Literal["auto", "always", "scroll", "hover"] | - | Describes the nature of scrollbar visibility, similar to how the scrollbar preferences in MacOS control visibility of native scrollbars. "auto", "always", "scroll", "hover". | | `scroll_hide_delay` | int | - | If type is set to either "scroll" or "hover", this prop determines the length of time, in milliseconds, before the scrollbars are hidden after the user stops interacting with scrollbars. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Spinner Source: http://localhost:3000/docs/library/data-display/spinner.md --- components: - rx.spinner --- Spinner is used to display an animated loading indicator when a task is in progress. ```python exec import reflex as rx ``` ```python demo rx.spinner() ``` ## Basic Examples Spinner can have different sizes. ```python demo rx.vstack( rx.hstack( rx.spinner(size="1"), rx.spinner(size="2"), rx.spinner(size="3"), align="center", gap="1em", ) ) ``` ## Demo with buttons Buttons have their own loading prop that automatically composes a spinner. ```python demo rx.button("Bookmark", loading=True) ``` ## Spinner inside a button If you have an icon inside the button, you can use the button's disabled state and wrap the icon in a standalone rx.spinner to achieve a more sophisticated design. ```python demo rx.button(rx.spinner(loading=True), "Bookmark", disabled=True) ``` ## API Reference ### rx.spinner A spinner component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `loading` | bool | - | If set, show an rx.spinner instead of the component children. | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the spinner. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Accordion Source: http://localhost:3000/docs/library/disclosure/accordion.md --- components: - rx.accordion.root - rx.accordion.item AccordionRoot: | lambda **props: rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content", **props, ), rx.accordion.item( header="Second Item", content="The second accordion item's content", **props, ), rx.accordion.item( header="Third item", content="The third accordion item's content", **props, ), width="300px", **props, ) AccordionItem: | lambda **props: rx.accordion.root( rx.accordion.item( header="Single Item", content="The accordion item's content", **props, ), width="300px", ) --- ```python exec import reflex as rx ``` An accordion is a vertically stacked set of interactive headings that each reveal an associated section of content. The accordion component is made up of `accordion`, which is the root of the component and takes in an `accordion.item`, which contains all the contents of the collapsible section. ## Basic Example ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), width="300px", ) ``` ## Styling ### Type We use the `type` prop to determine whether multiple items can be opened at once. The allowed values for this prop are `single` and `multiple` where `single` will only open one item at a time. The default value for this prop is `single`. ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", type="multiple", ) ``` ### Default Value We use the `default_value` prop to specify which item should open by default. The value for this prop should be any of the unique values set by an `accordion.item`. ```python demo rx.flex( rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content", value="item_1", ), rx.accordion.item( header="Second Item", content="The second accordion item's content", value="item_2", ), rx.accordion.item( header="Third item", content="The third accordion item's content", value="item_3", ), width="300px", default_value="item_2", ), direction="row", spacing="2", ) ``` ### Collapsible We use the `collapsible` prop to allow all items to close. If set to `False`, an opened item cannot be closed. ```python demo rx.flex( rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content" ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content" ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=False, width="300px", ), direction="row", spacing="2", ) ``` ### Disable We use the `disabled` prop to prevent interaction with the accordion and all its items. ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content" ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", disabled=True, ) ``` ### Orientation We use `orientation` prop to set the orientation of the accordion to `vertical` or `horizontal`. By default, the orientation will be set to `vertical`. Note that, the orientation prop won't change the visual orientation but the functional orientation of the accordion. This means that for vertical orientation, the up and down arrow keys moves focus between the next or previous item, while for horizontal orientation, the left or right arrow keys moves focus between items. ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", orientation="vertical", ) ``` ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", orientation="horizontal", ) ``` ### Variant ```python demo rx.flex( rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, variant="classic", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, variant="soft", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, variant="outline", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, variant="surface", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, variant="ghost", ), direction="row", spacing="2", ) ``` ### Color Scheme We use the `color_scheme` prop to assign a specific color to the accordion background, ignoring the global theme. ```python demo rx.flex( rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", color_scheme="grass", ), rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content" ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", color_scheme="green", ), direction="row", spacing="2", ) ``` ### Value We use the `value` prop to specify the controlled value of the accordion item that we want to activate. This property should be used in conjunction with the `on_value_change` event argument. ```python demo exec class AccordionState(rx.State): """The app state.""" value: str = "item_1" item_selected: str @rx.event def change_value(self, value): self.value = value self.item_selected = f"{value} selected" def index() -> rx.Component: return rx.theme( rx.container( rx.text(AccordionState.item_selected), rx.flex( rx.accordion.root( rx.accordion.item( header="Is it accessible?", content=rx.button("Test button"), value="item_1", ), rx.accordion.item( header="Is it unstyled?", content="Yes. It's unstyled by default, giving you freedom over the look and feel.", value="item_2", ), rx.accordion.item( header="Is it finished?", content="It's still in beta, but it's ready to use in production.", value="item_3", ), collapsible=True, width="300px", value=AccordionState.value, on_value_change=lambda value: AccordionState.change_value(value), ), direction="column", spacing="2", ), padding="2em", text_align="center", ) ) ``` ## AccordionItem The accordion item contains all the parts of a collapsible section. ## Styling ### Value ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content", value="item_1", ), rx.accordion.item( header="Second Item", content="The second accordion item's content", value="item_2", ), rx.accordion.item( header="Third item", content="The third accordion item's content", value="item_3", ), collapsible=True, width="300px", ) ``` ### Disable ```python demo rx.accordion.root( rx.accordion.item( header="First Item", content="The first accordion item's content", disabled=True, ), rx.accordion.item( header="Second Item", content="The second accordion item's content", ), rx.accordion.item( header="Third item", content="The third accordion item's content" ), collapsible=True, width="300px", color_scheme="blue", ) ``` ## API Reference ### rx.accordion.root An accordion component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `as_child` | bool | - | Change the default rendered element for the one passed as a child. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color scheme of the component. | | `variant` | Literal["classic", "soft", "surface", "outline", "ghost"] | - | The variant of the component. | | `type` | Literal["single", "multiple"] | - | The type of accordion (single or multiple). | | `value` | str, Sequence[str] | - | The value of the item to expand. | | `default_value` | str, Sequence[str] | - | The default value of the item to expand. | | `collapsible` | bool | - | Whether or not the accordion is collapsible. | | `disabled` | bool | - | Whether or not the accordion is disabled. | | `dir` | Literal["ltr", "rtl"] | - | The reading direction of the accordion when applicable. | | `orientation` | Literal["vertical", "horizontal"] | - | The orientation of the accordion. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | The radius of the accordion corners. | | `duration` | int | - | The time in milliseconds to animate open and close. | | `easing` | str | - | The easing function to use for the animation. | | `show_dividers` | bool | - | Whether to show divider lines between items. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_value_change` | Fired when the opened the accordions changes. | ### rx.accordion.item An accordion component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `as_child` | bool | - | Change the default rendered element for the one passed as a child. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color scheme of the component. | | `variant` | Literal["classic", "soft", "surface", "outline", "ghost"] | - | The variant of the component. | | `value` | str | - | A unique identifier for the item. | | `disabled` | bool | - | When true, prevents the user from interacting with the item. | | `header` | Component, str | - | The header of the accordion item. | | `content` | str, Component, NoneType | - | The content of the accordion item. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Segmented Control Source: http://localhost:3000/docs/library/disclosure/segmented-control.md --- components: - rx.segmented_control.root - rx.segmented_control.item SegmentedControlRoot: | lambda **props: rx.segmented_control.root( rx.segmented_control.item("Inbox", value="inbox"), rx.segmented_control.item("Drafts", value="drafts"), rx.segmented_control.item("Sent", value="sent"), default_value="inbox", **props, ) SegmentedControlItem: | lambda **props: rx.segmented_control.root( rx.segmented_control.item("Inbox", value="inbox", **props), rx.segmented_control.item("Drafts", value="drafts"), default_value="inbox", ) --- ```python exec import reflex as rx class SegmentedState(rx.State): """The app state.""" control: str = "test" @rx.event def set_control(self, value: str | list[str]): self.control = value ``` Segmented Control offers a clear and accessible way to switch between predefined values and views, e.g., "Inbox," "Drafts," and "Sent." With Segmented Control, you can make mutually exclusive choices, where only one option can be active at a time, clear and accessible. Without Segmented Control, end users might have to deal with controls like dropdowns or multiple buttons that don't clearly convey state or group options together visually. ## Basic Example The `Segmented Control` component is made up of a `rx.segmented_control.root` which groups `rx.segmented_control.item`. The `rx.segmented_control.item` components define the individual segments of the control, each with a label and a unique value. ```python demo rx.vstack( rx.segmented_control.root( rx.segmented_control.item("Home", value="home"), rx.segmented_control.item("About", value="about"), rx.segmented_control.item("Test", value="test"), on_change=SegmentedState.set_control, value=SegmentedState.control, ), rx.card( rx.text(SegmentedState.control, align="left"), rx.text(SegmentedState.control, align="center"), rx.text(SegmentedState.control, align="right"), width="100%", ), ) ``` **In the example above:** `on_change` is used to specify a callback function that will be called when the user selects a different segment. In this case, the `SegmentedState.set_control` event handler is used to update the `control` state variable when the user changes the selected segment. `value` prop is used to specify the currently selected segment, which is bound to the `SegmentedState.control` state variable. ## API Reference ### rx.segmented_control.root Root element for a SegmentedControl component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the segmented control: "1", "2", "3". | | `variant` | Literal["classic", "surface"] | - | Variant of button: "classic", "surface". | | `type` | Literal["single", "multiple"] | - | The type of the segmented control, either "single" for selecting one option or "multiple" for selecting multiple options. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Override theme color for button. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | The radius of the segmented control: "none", "small", "medium", "large", "full". | | `default_value` | str, Sequence[str] | - | The default value of the segmented control. | | `value` | str, Sequence[str] | - | The current value of the segmented control. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_change` | Handles the `onChange` event for the SegmentedControl component. | ### rx.segmented_control.item An item in the SegmentedControl component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | str | - | The value of the item. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Tabs Source: http://localhost:3000/docs/library/disclosure/tabs.md --- components: - rx.tabs.root - rx.tabs.list - rx.tabs.trigger - rx.tabs.content only_low_level: - True TabsRoot: | lambda **props: rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Account", value="account"), rx.tabs.trigger("Documents", value="documents"), rx.tabs.trigger("Settings", value="settings"), ), rx.box( rx.tabs.content( rx.text("Make changes to your account"), value="account", ), rx.tabs.content( rx.text("Update your documents"), value="documents", ), rx.tabs.content( rx.text("Edit your personal profile"), value="settings", ), ), **props, ) TabsList: | lambda **props: rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Account", value="account"), rx.tabs.trigger("Documents", value="documents"), rx.tabs.trigger("Settings", value="settings"), **props, ), rx.box( rx.tabs.content( rx.text("Make changes to your account"), value="account", ), rx.tabs.content( rx.text("Update your documents"), value="documents", ), rx.tabs.content( rx.text("Edit your personal profile"), value="settings", ), ), ) TabsTrigger: | lambda **props: rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Account", value="account", **props), rx.tabs.trigger("Documents", value="documents", **props), rx.tabs.trigger("Settings", value="settings", **props), ), rx.box( rx.tabs.content( rx.text("Make changes to your account"), value="account", ), rx.tabs.content( rx.text("Update your documents"), value="documents", ), rx.tabs.content( rx.text("Edit your personal profile"), value="settings", ), ), ) TabsContent: | lambda **props: rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Account", value="account"), rx.tabs.trigger("Documents", value="documents"), rx.tabs.trigger("Settings", value="settings"), ), rx.box( rx.tabs.content( rx.text("Make changes to your account"), value="account", **props, ), rx.tabs.content( rx.text("Update your documents"), value="documents", **props, ), rx.tabs.content( rx.text("Edit your personal profile"), value="settings", **props, ), ), ) --- ```python exec import reflex as rx ``` Tabs are a set of layered sections of content—known as tab panels that are displayed one at a time. They facilitate the organization and navigation between sets of content that share a connection and exist at a similar level of hierarchy. ## Basic Example ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2") ), rx.tabs.content( rx.text("item on tab 1"), value="tab1", ), rx.tabs.content( rx.text("item on tab 2"), value="tab2", ), ) ``` The `tabs` component is made up of a `rx.tabs.root` which groups `rx.tabs.list` and `rx.tabs.content` parts. ## Styling ### Default value We use the `default_value` prop to set a default active tab, this will select the specified tab by default. ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2") ), rx.tabs.content( rx.text("item on tab 1"), value="tab1", ), rx.tabs.content( rx.text("item on tab 2"), value="tab2", ), default_value="tab2", ) ``` ### Orientation We use `orientation` prop to set the orientation of the tabs component to `vertical` or `horizontal`. By default, the orientation will be set to `horizontal`. Setting this value will change both the visual orientation and the functional orientation. ```md alert info The functional orientation means for `vertical`, the `up` and `down` arrow keys moves focus between the next or previous tab, while for `horizontal`, the `left` and `right` arrow keys moves focus between tabs. ``` ```md alert warning # When using vertical orientation, make sure to assign a tabs.content for each trigger. Defining triggers without content will result in a visual bug where the width of the triggers list isn't constant. ``` ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2") ), rx.tabs.content( rx.text("item on tab 1"), value="tab1", ), rx.tabs.content( rx.text("item on tab 2"), value="tab2", ), default_value="tab1", orientation="vertical", ) ``` ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2") ), rx.tabs.content( rx.text("item on tab 1"), value="tab1", ), rx.tabs.content( rx.text("item on tab 2"), value="tab2", ), default_value="tab1", orientation="horizontal", ) ``` ### Value We use the `value` prop to specify the controlled value of the tab that we want to activate. This property should be used in conjunction with the `on_change` event argument. ```python demo exec class TabsState(rx.State): """The app state.""" value = "tab1" tab_selected = "" @rx.event def change_value(self, val): self.tab_selected = f"{val} clicked!" self.value = val def index() -> rx.Component: return rx.container( rx.flex( rx.text(f"{TabsState.value} clicked !"), rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2"), ), rx.tabs.content( rx.text("items on tab 1"), value="tab1", ), rx.tabs.content( rx.text("items on tab 2"), value="tab2", ), default_value="tab1", value=TabsState.value, on_change=lambda x: TabsState.change_value(x), ), direction="column", spacing="2", ), padding="2em", font_size="2em", text_align="center", ) ``` ## Tablist The Tablist is used to list the respective tabs to the tab component ## Tab Trigger This is the button that activates the tab's associated content. It is typically used in the `Tablist` ## Styling ### Value We use the `value` prop to assign a unique value that associates the trigger with content. ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2"), rx.tabs.trigger("Tab 3", value="tab3"), ), ) ``` ### Disable We use the `disabled` prop to disable the tab. ```python demo rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2"), rx.tabs.trigger("Tab 3", value="tab3", disabled=True), ), ) ``` ## Tabs Content Contains the content associated with each trigger. ## Styling ### Value We use the `value` prop to assign a unique value that associates the content with a trigger. ```python rx.tabs.root( rx.tabs.list( rx.tabs.trigger("Tab 1", value="tab1"), rx.tabs.trigger("Tab 2", value="tab2") ), rx.tabs.content( rx.text("item on tab 1"), value="tab1", ), rx.tabs.content( rx.text("item on tab 2"), value="tab2", ), default_value="tab1", orientation="vertical", ) ``` ## API Reference ### rx.tabs.root Set of content sections to be displayed one at a time. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `default_value` | str | - | The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs. | | `value` | str | - | The controlled value of the tab that should be active. Use when you need to control the state of the tabs. | | `orientation` | Literal["vertical", "horizontal"] | - | The orientation of the tabs. | | `dir` | Literal["ltr", "rtl"] | - | Reading direction of the tabs. | | `activation_mode` | Literal["automatic", "manual"] | - | The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_change` | Fired when the value of the tabs changes. | ### rx.tabs.list Contains the triggers that sit alongside the active content. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | Literal["1", "2"], Breakpoints[str, Literal["1", "2"]] | - | Tabs size "1" - "2". | | `loop` | bool | - | When true, the tabs will loop when reaching the end. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.tabs.trigger The button that activates its associated content. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | str | - | The value of the tab. Must be unique for each tab. | | `disabled` | bool | - | Whether the tab is disabled. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color of the line under the tab when active. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.tabs.content Contains the content associated with each trigger. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | str | - | The value of the tab. Must be unique for each tab. | | `force_mount` | bool | - | Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Auto Scroll Source: http://localhost:3000/docs/library/dynamic-rendering/auto-scroll.md --- components: - rx.auto_scroll --- ```python exec import reflex as rx ``` The `rx.auto_scroll` component is a div that automatically scrolls to the bottom when new content is added. This is useful for chat interfaces, logs, or any container where new content is dynamically added and you want to ensure the most recent content is visible. ## Basic Usage ```python demo exec import reflex as rx class AutoScrollState(rx.State): messages: list[str] = ["Initial message"] def add_message(self): self.messages.append(f"New message #{len(self.messages) + 1}") def auto_scroll_example(): return rx.vstack( rx.auto_scroll( rx.foreach( AutoScrollState.messages, lambda message: rx.box( message, padding="0.5em", border_bottom="1px solid #eee", width="100%", ), ), height="200px", width="300px", border="1px solid #ddd", border_radius="md", ), rx.button("Add Message", on_click=AutoScrollState.add_message), width="300px", align_items="center", ) ``` The `auto_scroll` component automatically scrolls to show the newest content when it's added. In this example, each time you click "Add Message", a new message is added to the list and the container automatically scrolls to display it. ## When to Use Auto Scroll - **Chat applications**: Keep the chat window scrolled to the most recent messages. - **Log viewers**: Automatically follow new log entries as they appear. - **Feed interfaces**: Keep the newest content visible in dynamically updating feeds. ## Props `rx.auto_scroll` is based on the `rx.div` component and inherits all of its props. By default, it sets `overflow="auto"` to enable scrolling. Some common props you might use with `auto_scroll`: - `height`: Set the height of the scrollable container. - `width`: Set the width of the scrollable container. - `padding`: Add padding inside the container. - `border`: Add a border around the container. - `border_radius`: Round the corners of the container. ## How It Works The component tracks when new content is added and maintains the scroll position in two scenarios: 1. When the user is already near the bottom of the content (within 50 pixels), it will scroll to the bottom when new content is added. 2. When the container didn't have a scrollbar before but does now (due to new content), it will automatically scroll to the bottom. This behavior ensures that users can scroll up to view older content without being forced back to the bottom, while still automatically following new content in most cases. ## API Reference ### rx.auto_scroll A div that automatically scrolls to the bottom when new content is added. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Cond Source: http://localhost:3000/docs/library/dynamic-rendering/cond.md --- components: - rx.cond --- ```python exec import reflex as rx ``` This component is used to conditionally render components. The cond component takes a condition and two components. If the condition is `True`, the first component is rendered, otherwise the second component is rendered. ```python demo exec class CondState(rx.State): show: bool = True @rx.event def change(self): self.show = not (self.show) def cond_example(): return rx.vstack( rx.button("Toggle", on_click=CondState.change), rx.cond( CondState.show, rx.text("Text 1", color="blue"), rx.text("Text 2", color="red"), ), ) ``` The second component is optional and can be omitted. If it is omitted, nothing is rendered if the condition is `False`. ```python demo exec class CondOptionalState(rx.State): show_optional: bool = True @rx.event def toggle_optional(self): self.show_optional = not (self.show_optional) def cond_optional_example(): return rx.vstack( rx.button("Toggle", on_click=CondOptionalState.toggle_optional), rx.cond( CondOptionalState.show_optional, rx.text("This text appears when condition is True", color="green"), ), rx.text("This text is always visible", color="gray"), ) ``` ```md video https://youtube.com/embed/ITOZkzjtjUA?start=6040&end=6463 # Video: Conditional Rendering ``` ## Negation You can use the logical operator `~` to negate a condition. ```python rx.vstack( rx.button("Toggle", on_click=CondState.change), rx.cond( CondState.show, rx.text("Text 1", color="blue"), rx.text("Text 2", color="red") ), rx.cond( ~CondState.show, rx.text("Text 1", color="blue"), rx.text("Text 2", color="red") ), ) ``` ## Multiple Conditions It is also possible to make up complex conditions using the `logical or` (|) and `logical and` (&) operators. Here we have an example using the var operators `>=`, `<=`, `&`. We define a condition that if a person has an age between 18 and 65, including those ages, they are able to work, otherwise they cannot. We could equally use the operator `|` to represent a `logical or` in one of our conditions. ```python demo exec import random class CondComplexState(rx.State): age: int = 19 @rx.event def change(self): self.age = random.randint(0, 100) def cond_complex_example(): return rx.vstack( rx.button("Toggle", on_click=CondComplexState.change), rx.text(f"Age: {CondComplexState.age}"), rx.cond( (CondComplexState.age >= 18) & (CondComplexState.age <= 65), rx.text("You can work!", color="green"), rx.text("You cannot work!", color="red"), ), ) ``` ## Nested Conditional We can also nest `cond` components within each other to create more complex logic. In python we can have an `if` statement that then has several `elif` statements before finishing with an `else`. This is also possible in reflex using nested `cond` components. In this example we check whether a number is positive, negative or zero. Here is the python logic using `if` statements: ```python number = 0 if number > 0: print("Positive number") elif number == 0: print("Zero") else: print("Negative number") ``` This reflex code that is logically identical: ```python demo exec import random class NestedState(rx.State): num: int = 0 def change(self): self.num = random.randint(-10, 10) def cond_nested_example(): return rx.vstack( rx.button("Toggle", on_click=NestedState.change), rx.cond( NestedState.num > 0, rx.text(f"{NestedState.num} is Positive!", color="orange"), rx.cond( NestedState.num == 0, rx.text(f"{NestedState.num} is Zero!", color="blue"), rx.text(f"{NestedState.num} is Negative!", color="red"), ), ), ) ``` Here is a more advanced example where we have three numbers and we are checking which of the three is the largest. If any two of them are equal then we return that `Some of the numbers are equal!`. The reflex code that follows is logically identical to doing the following in python: ```python a = 8 b = 10 c = 2 if (a > b and a > c) and (a != b and a != c): print(a, " is the largest!") elif (b > a and b > c) and (b != a and b != c): print(b, " is the largest!") elif (c > a and c > b) and (c != a and c != b): print(c, " is the largest!") else: print("Some of the numbers are equal!") ``` ## API Reference ### rx.cond Render one of two components based on a condition. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `cond` | Any | - | The cond to determine which component to render. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Foreach Source: http://localhost:3000/docs/library/dynamic-rendering/foreach.md ```python exec import reflex as rx ``` 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 # Use `rx.foreach` for state vars; use Python list or dict comprehensions for constants. ``` ```python demo exec 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 demo exec 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 demo exec 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 demo exec 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", as_="h2"), 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 demo exec 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 demo exec 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. # Match Source: http://localhost:3000/docs/library/dynamic-rendering/match.md --- components: - rx.match --- ```python exec import reflex as rx ``` The `rx.match` feature in Reflex serves as a powerful alternative to `rx.cond` for handling conditional statements. While `rx.cond` excels at conditionally rendering two components based on a single condition, `rx.match` extends this functionality by allowing developers to handle multiple conditions and their associated components. This feature is especially valuable when dealing with intricate conditional logic or nested scenarios, where the limitations of `rx.cond` might lead to less readable and more complex code. With `rx.match`, developers can not only handle multiple conditions but also perform structural pattern matching, making it a versatile tool for managing various scenarios in Reflex applications. ## Basic Usage The `rx.match` function provides a clear and expressive syntax for handling multiple conditions and their corresponding components: ```python rx.match( condition, (case_1, component_1), (case_2, component_2), ... default_component, ) ``` - `condition`: The value to match against. - `(case_i, component_i)`: A Tuple of matching cases and their corresponding return components. - `default_component`: A special case for the default component when the condition isn't matched by any of the match cases. Example ```python demo exec from typing import List import reflex as rx class MatchState(rx.State): cat_breed: str = "" animal_options: List[str] = [ "persian", "siamese", "maine coon", "ragdoll", "pug", "corgi", ] @rx.event def set_cat_breed(self, breed: str): self.cat_breed = breed def match_demo(): return rx.flex( rx.match( MatchState.cat_breed, ("persian", rx.text("Persian cat selected.")), ("siamese", rx.text("Siamese cat selected.")), ("maine coon", rx.text("Maine Coon cat selected.")), ("ragdoll", rx.text("Ragdoll cat selected.")), rx.text("Unknown cat breed selected."), ), rx.select.root( rx.select.trigger(), rx.select.content( rx.select.group( rx.foreach( MatchState.animal_options, lambda x: rx.select.item(x, value=x) ) ), ), value=MatchState.cat_breed, on_change=MatchState.set_cat_breed, ), direction="column", gap="2", ) ``` ## Default Case The default case in `rx.match` serves as a universal handler for scenarios where none of the specified match cases aligns with the given match condition. Here are key considerations when working with the default case: - **Placement in the Match Function**: The default case must be the last non-tuple argument in the `rx.match` component. All match cases should be enclosed in tuples; any non-tuple value is automatically treated as the default case. For example: ```python rx.match( MatchState.cat_breed, ("persian", rx.text("persian cat selected")), rx.text("Unknown cat breed selected."), ("siamese", rx.text("siamese cat selected")), ) ``` The above code snippet will result in an error due to the misplaced default case. - **Single Default Case**: Only one default case is allowed in the `rx.match` component. Attempting to specify multiple default cases will lead to an error. For instance: ```python rx.match( MatchState.cat_breed, ("persian", rx.text("persian cat selected")), ("siamese", rx.text("siamese cat selected")), rx.text("Unknown cat breed selected."), rx.text("Another unknown cat breed selected."), ) ``` - **Optional Default Case for Component Return Values**: If the match cases in a `rx.match` component return components, the default case can be optional. In this scenario, if a default case is not provided, `rx.fragment` will be implicitly assigned as the default. For example: ```python rx.match( MatchState.cat_breed, ("persian", rx.text("persian cat selected")), ("siamese", rx.text("siamese cat selected")), ) ``` In this case, `rx.fragment` is the default case. However, not providing a default case for non-component return values will result in an error: ```python rx.match( MatchState.cat_breed, ("persian", "persian cat selected"), ("siamese", "siamese cat selected"), ) ``` The above code snippet will result in an error as a default case must be explicitly provided in this scenario. ## Handling Multiple Conditions `rx.match` excels in efficiently managing multiple conditions and their corresponding components, providing a cleaner and more readable alternative compared to nested `rx.cond` structures. Consider the following example: ```python demo exec from typing import List import reflex as rx class MultiMatchState(rx.State): animal_breed: str = "" animal_options: List[str] = [ "persian", "siamese", "maine coon", "pug", "corgi", "mustang", "rahvan", "football", "golf", ] @rx.event def set_animal_breed(self, breed: str): self.animal_breed = breed def multi_match_demo(): return rx.flex( rx.match( MultiMatchState.animal_breed, ("persian", "siamese", "maine coon", rx.text("Breeds of cats.")), ("pug", "corgi", rx.text("Breeds of dogs.")), ("mustang", "rahvan", rx.text("Breeds of horses.")), rx.text("Unknown animal breed"), ), rx.select.root( rx.select.trigger(), rx.select.content( rx.select.group( rx.foreach( MultiMatchState.animal_options, lambda x: rx.select.item(x, value=x), ) ), ), value=MultiMatchState.animal_breed, on_change=MultiMatchState.set_animal_breed, ), direction="column", gap="2", ) ``` In a match case tuple, you can specify multiple conditions. The last value of the match case tuple is automatically considered as the return value. It's important to note that a match case tuple should contain a minimum of two elements: a match case and a return value. The following code snippet will result in an error: ```python rx.match( MatchState.cat_breed, ("persian",), ("maine coon", rx.text("Maine Coon cat selected")), ) ``` ## Usage as Props Similar to `rx.cond`, `rx.match` can be used as prop values, allowing dynamic behavior for UI components: ```python demo exec import reflex as rx class MatchPropState(rx.State): value: int = 0 @rx.event def incr(self): self.value += 1 @rx.event def decr(self): self.value -= 1 def match_prop_demo_(): return rx.flex( rx.button("decrement", on_click=MatchPropState.decr, background_color="red"), rx.badge( MatchPropState.value, color_scheme=rx.match( MatchPropState.value, (1, "red"), (2, "blue"), (6, "purple"), (10, "orange"), "green", ), size="2", ), rx.button("increment", on_click=MatchPropState.incr), align_items="center", direction="row", gap="3", ) ``` In the example above, the background color property of the box component containing `State.value` changes to red when `state.value` is 1, blue when its 5, green when its 5, orange when its 15 and black for any other value. The example below also shows handling multiple conditions with the match component as props. ```python demo exec import reflex as rx class MatchMultiPropState(rx.State): value: int = 0 @rx.event def incr(self): self.value += 1 @rx.event def decr(self): self.value -= 1 def match_multi_prop_demo_(): return rx.flex( rx.button( "decrement", on_click=MatchMultiPropState.decr, background_color="red" ), rx.badge( MatchMultiPropState.value, color_scheme=rx.match( MatchMultiPropState.value, (1, 3, 9, "red"), (2, 4, 5, "blue"), (6, 8, 12, "purple"), (10, 15, 20, 25, "orange"), "green", ), size="2", ), rx.button("increment", on_click=MatchMultiPropState.incr), align_items="center", direction="row", gap="3", ) ``` ```md alert warning # Usage with Structural Pattern Matching The `rx.match` component is designed for structural pattern matching. If the value of your match condition evaluates to a boolean (True or False), it is recommended to use `rx.cond` instead. Consider the following example, which is more suitable for `rx.cond`:\* ``` ```python rx.cond(MatchPropState.value == 10, "true value", "false value") ``` ## API Reference ### rx.match Match cases based on a condition. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `cond` | Any | - | The condition to determine which case to match. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Button Source: http://localhost:3000/docs/library/forms/button.md --- components: - rx.button Button: | lambda **props: rx.button("Basic Button", **props) --- ```python exec import reflex as rx ``` Buttons are essential elements in your application's user interface that users can click to trigger events. ## Basic Example The `on_click` trigger is called when the button is clicked. ```python demo exec class CountState(rx.State): count: int = 0 @rx.event def increment(self): self.count += 1 @rx.event def decrement(self): self.count -= 1 def counter(): return rx.flex( rx.button( "Decrement", color_scheme="red", on_click=CountState.decrement, ), rx.heading(CountState.count, as_="h2"), rx.button( "Increment", color_scheme="grass", on_click=CountState.increment, ), spacing="3", ) ``` ### Loading and Disabled The `loading` prop is used to indicate that the action triggered by the button is currently in progress. When set to `True`, the button displays a loading spinner, providing visual feedback to the user that the action is being processed. This also prevents multiple clicks while the button is in the loading state. By default, `loading` is set to `False`. The `disabled` prop also prevents the button from being but does not provide a spinner. ```python demo rx.flex( rx.button("Regular"), rx.button("Loading", loading=True), rx.button("Disabled", disabled=True), spacing="2", ) ``` ## API Reference ### rx.button Trigger an action or event, such as submitting a form or displaying a dialog. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `loading` | bool | - | If set, show an rx.spinner instead of the component children. | | `auto_focus` | bool | - | Automatically focuses the button when the page loads. | | `disabled` | bool | - | Disables the button. | | `form` | str | - | Associates the button with a form (by id). | | `form_action` | str | - | URL to send the form data to (for type="submit" buttons). | | `form_enc_type` | str | - | How the form data should be encoded when submitting to the server (for type="submit" buttons). | | `form_method` | str | - | HTTP method to use for sending form data (for type="submit" buttons). | | `form_no_validate` | bool | - | Bypasses form validation when submitting (for type="submit" buttons). | | `form_target` | str | - | Specifies where to display the response after submitting the form (for type="submit" buttons). | | `name` | str | - | Name of the button, used when sending form data. | | `type` | Literal["submit", "reset", "button"] | - | Type of the button (submit, reset, or button). | | `value` | str, int, float | - | Value of the button, used when sending form data. | | `as_child` | bool | - | Change the default rendered element for the one passed as a child, merging their props and behavior. | | `size` | Literal["1", "2", "3", "4"], Breakpoints[str, Literal["1", "2", "3", "4"]] | - | Button size "1" - "4". | | `variant` | Literal["classic", "solid", "soft", "surface", "outline", "ghost"] | - | Variant of button: "solid", "soft", "outline", "ghost". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Override theme color for button. | | `high_contrast` | bool | - | Whether to render the button with higher contrast color against background. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | Override theme radius for button: "none", "small", "medium", "large", "full". | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Checkbox Source: http://localhost:3000/docs/library/forms/checkbox.md --- components: - rx.checkbox HighLevelCheckbox: | lambda **props: rx.checkbox("Basic Checkbox", **props) --- ```python exec import reflex as rx ``` ## Basic Example The `on_change` trigger is called when the `checkbox` is clicked. ```python demo exec class CheckboxState(rx.State): checked: bool = False @rx.event def set_checked(self, value: bool): self.checked = value def checkbox_example(): return rx.vstack( rx.heading(CheckboxState.checked, as_="h2"), rx.checkbox(on_change=CheckboxState.set_checked), ) ``` The `input` prop is used to set the `checkbox` as a controlled component. ```python demo exec class FormCheckboxState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): """Handle the form submit.""" print(form_data) self.form_data = form_data def form_checkbox_example(): return rx.card( rx.vstack( rx.heading("Example Form", as_="h2"), rx.form.root( rx.hstack( rx.checkbox( name="checkbox", label="Accept terms and conditions", ), rx.button("Submit", type="submit"), width="100%", ), on_submit=FormCheckboxState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.hstack( rx.heading("Results:", as_="h2"), rx.badge(FormCheckboxState.form_data.to_string()), ), align_items="left", width="100%", ), width="50%", ) ``` ## API Reference ### rx.checkbox A checkbox component with a label. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `text` | str | - | The text label for the checkbox. | | `spacing` | Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] | - | The gap between the checkbox and the label. | | `size` | Literal["1", "2", "3"] | - | The size of the checkbox "1" - "3". | | `as_child` | bool | - | Change the default rendered element for the one passed as a child, merging their props and behavior. | | `variant` | Literal["classic", "surface", "soft"] | - | Variant of checkbox: "classic", "surface", "soft". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Override theme color for checkbox. | | `high_contrast` | bool | - | Whether to render the checkbox with higher contrast color against background. | | `default_checked` | bool | - | Whether the checkbox is checked by default. | | `checked` | bool | - | Whether the checkbox is checked. | | `disabled` | bool | - | Whether the checkbox is disabled. | | `required` | bool | - | Whether the checkbox is required. | | `name` | str | - | The name of the checkbox control when submitting the form. | | `value` | str | - | The value of the checkbox control when submitting the form. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_change` | Fired when the checkbox is checked or unchecked. | # type attribute is required for "typeMismatch" validation Source: http://localhost:3000/docs/library/forms/form.md --- components: - rx.form FormRoot: | lambda **props: rx.form.root( rx.form.field( rx.flex( rx.form.label("Email"), rx.form.control( rx.input( placeholder="Email Address", # type attribute is required for "typeMismatch" validation type="email", ), as_child=True, ), rx.form.message("Please enter a valid email"), rx.form.submit( rx.button("Submit"), as_child=True, ), direction="column", spacing="2", align="stretch", ), name="email", ), **props, ) FormField: | lambda **props: rx.form.root( rx.form.field( rx.flex( rx.form.label("Email"), rx.form.control( rx.input( placeholder="Email Address", # type attribute is required for "typeMismatch" validation type="email", ), as_child=True, ), rx.form.message("Please enter a valid email", match="typeMismatch"), rx.form.submit( rx.button("Submit"), as_child=True, ), direction="column", spacing="2", align="stretch", ), **props, ), reset_on_submit=True, ) FormLabel: | lambda **props: rx.form.root( rx.form.field( rx.flex( rx.form.label("Email", **props,), rx.form.control( rx.input( placeholder="Email Address", # type attribute is required for "typeMismatch" validation type="email", ), as_child=True, ), rx.form.message("Please enter a valid email", match="typeMismatch"), rx.form.submit( rx.button("Submit"), as_child=True, ), direction="column", spacing="2", align="stretch", ), ), reset_on_submit=True, ) FormMessage: | lambda **props: rx.form.root( rx.form.field( rx.flex( rx.form.label("Email"), rx.form.control( rx.input( placeholder="Email Address", # type attribute is required for "typeMismatch" validation type="email", ), as_child=True, ), rx.form.message("Please enter a valid email", **props,), rx.form.submit( rx.button("Submit"), as_child=True, ), direction="column", spacing="2", align="stretch", ), name="email", ), on_submit=lambda form_data: rx.window_alert(form_data.to_string()), reset_on_submit=True, ) --- ```python exec import reflex as rx ``` Forms are used to collect user input. The `rx.form` component is used to group inputs and submit them together. The form component's children can be form controls such as `rx.input`, `rx.checkbox`, `rx.slider`, `rx.text_area`, `rx.radio_group`, `rx.select` or `rx.switch`. The controls should have a `name` attribute that is used to identify the control in the form data. The `on_submit` event trigger submits the form data as a dictionary to the `handle_submit` event handler. The form is submitted when the user clicks the submit button or presses enter on the form controls. ```python demo exec class FormState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): """Handle the form submit.""" self.form_data = form_data def form_example(): return rx.vstack( rx.form( rx.vstack( rx.input(placeholder="First Name", name="first_name"), rx.input(placeholder="Last Name", name="last_name"), rx.hstack( rx.checkbox("Checked", name="check"), rx.switch("Switched", name="switch"), ), rx.button("Submit", type="submit"), ), on_submit=FormState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.heading("Results", as_="h2"), rx.text(FormState.form_data.to_string()), ) ``` ```md alert warning # When using the form you must include a button or input with `type='submit'`. ``` ```md alert info # Using `name` vs `id`. When using the `name` attribute in form controls like `rx.switch`, `rx.radio_group`, and `rx.checkbox`, these controls will only be included in the form data if their values are set (e.g., if the checkbox is checked, the switch is toggled, or a radio option is selected). If you need these controls to be passed in the form data even when their values are not set, you can use the `id` attribute instead of name. The id attribute ensures that the control is always included in the submitted form data, regardless of whether its value is set or not. ``` ```md video https://youtube.com/embed/ITOZkzjtjUA?start=5287&end=6040 # Video: Forms ``` ## Validating Form Data with a TypedDict The `on_submit` handler usually receives the form data as a plain `dict`, which means accessing a field is untyped (`form_data["email"]` returns `Any`) and a typo in a `name` goes unnoticed until runtime. Instead, you can annotate the handler's parameter with a [`TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict). This gives you typed, autocompleted access to each field inside the handler, and Reflex validates the form **at compile time**: every required key of the `TypedDict` must have a matching form control. If a required field has no control with that `name` (or `id`), Reflex raises an `EventHandlerValueError` before the app starts, pointing out exactly which fields are missing. ```python demo exec from typing import TypedDict from typing_extensions import NotRequired class ContactForm(TypedDict): first_name: str last_name: str email: str message: NotRequired[str] # optional field class TypedFormState(rx.State): form_data: ContactForm | None = None @rx.event def handle_submit(self, form_data: ContactForm): """Handle the form submit.""" # form_data is typed: editors autocomplete the keys below. self.form_data = form_data def typed_form_example(): return rx.vstack( rx.form( rx.vstack( rx.input(placeholder="First Name", name="first_name"), rx.input(placeholder="Last Name", name="last_name"), rx.input(placeholder="Email", name="email", type="email"), rx.text_area(placeholder="Message", name="message"), rx.button("Submit", type="submit"), ), on_submit=TypedFormState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.heading("Results", as_="h2"), rx.text(TypedFormState.form_data.to_string()), ) ``` ### Required and optional fields By default every key declared in a `TypedDict` is **required** and must be backed by a form control. Mark a field as optional with `NotRequired` (or by inheriting from a `total=False` base) so Reflex won't require a matching control: ```python from typing import TypedDict from typing_extensions import NotRequired class ContactForm(TypedDict): name: str # required: a control named "name" must exist email: str # required: a control named "email" must exist message: NotRequired[str] # optional: no control required ``` If a required field is missing, creating the form fails fast with a message that lists the expected, missing, and matching fields: ```python class SignupForm(TypedDict): username: str email: str class SignupState(rx.State): @rx.event def handle_submit(self, form_data: SignupForm): ... # Raises EventHandlerValueError: the form has no control named "email". rx.form( rx.input(name="username"), rx.button("Submit", type="submit"), on_submit=SignupState.handle_submit, ) ``` ```md alert info # When is validation skipped? The check only runs when the form fields are statically known. It is automatically skipped when control `name`/`id` values are dynamic (for example, built with `rx.foreach`), or when the form has an `id` (since controls can be associated from elsewhere via the HTML `form` attribute). In those cases the `TypedDict` still provides typed access inside the handler. At runtime `form_data` is always a regular dictionary. ``` ## Dynamic Forms Forms can be dynamically created by iterating through state vars using `rx.foreach`. This example allows the user to add new fields to the form prior to submit, and all fields will be included in the form data passed to the `handle_submit` function. ```python demo exec class DynamicFormState(rx.State): form_data: dict = {} form_fields: list[str] = ["first_name", "last_name", "email"] @rx.var(cache=True) def form_field_placeholders(self) -> list[str]: return [ " ".join(w.capitalize() for w in field.split("_")) for field in self.form_fields ] @rx.event def add_field(self, form_data: dict): new_field = form_data.get("new_field") if not new_field: return field_name = new_field.strip().lower().replace(" ", "_") self.form_fields.append(field_name) @rx.event def handle_submit(self, form_data: dict): self.form_data = form_data def dynamic_form(): return rx.vstack( rx.form( rx.vstack( rx.foreach( DynamicFormState.form_fields, lambda field, idx: rx.input( placeholder=DynamicFormState.form_field_placeholders[idx], name=field, ), ), rx.button("Submit", type="submit"), ), on_submit=DynamicFormState.handle_submit, reset_on_submit=True, ), rx.form( rx.hstack( rx.input(placeholder="New Field", name="new_field"), rx.button("+", type="submit"), ), on_submit=DynamicFormState.add_field, reset_on_submit=True, ), rx.divider(), rx.heading("Results", as_="h2"), rx.text(DynamicFormState.form_data.to_string()), ) ``` ## API Reference ### rx.form The Form component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `as_child` | bool | - | Change the default rendered element for the one passed as a child. | | `accept` | str | - | MIME types the server accepts for file upload. | | `accept_charset` | str | - | Character encodings to be used for form submission. | | `action` | str | - | URL where the form's data should be submitted. | | `auto_complete` | str | - | Whether the form should have autocomplete enabled. | | `enc_type` | str | - | Encoding type for the form data when submitted. | | `method` | str | - | HTTP method to use for form submission. | | `name` | str | - | Name of the form. | | `no_validate` | bool | - | Indicates that the form should not be validated on submit. | | `target` | str | - | Where to display the response after submitting the form. | | `reset_on_submit` | bool | - | If true, the form will be cleared after submit. | | `handle_submit_unique_name` | str | - | The name used to make this form's submit handler function unique. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_submit` | Fired when the form is submitted. | | `on_clear_server_errors` | Fired when the errors are cleared. | # Input Source: http://localhost:3000/docs/library/forms/input.md --- components: - rx.input TextFieldRoot: | lambda **props: rx.input(placeholder="Search the docs", **props) TextFieldSlot: | lambda **props: rx.input( rx.input.slot( rx.icon(tag="search", height="16", width="16"), **props, ), placeholder="Search the docs", ) --- ```python exec import reflex as rx ``` The `input` component is an input field that users can type into. ```md video https://youtube.com/embed/ITOZkzjtjUA?start=1517&end=1869 # Video: Input ``` ## Basic Example The `on_blur` event handler is called when focus has left the `input` for example, it’s called when the user clicks outside of a focused text input. ```python demo exec class TextfieldBlur(rx.State): text: str = "Hello World!" @rx.event def set_text(self, value: str): self.text = value def blur_example(): return rx.vstack( rx.heading(TextfieldBlur.text, as_="h2"), rx.input( placeholder="Search here...", on_blur=TextfieldBlur.set_text, ), ) ``` The `on_change` event handler is called when the `value` of `input` has changed. ```python demo exec class TextfieldControlled(rx.State): text: str = "Hello World!" @rx.event def set_text(self, value: str): self.text = value def controlled_example(): return rx.vstack( rx.heading(TextfieldControlled.text, as_="h2"), rx.input( placeholder="Search here...", value=TextfieldControlled.text, on_change=TextfieldControlled.set_text, ), ) ``` Behind the scenes, the input component is implemented as a debounced input to avoid sending individual state updates per character to the backend while the user is still typing. This allows a state variable to directly control the `value` prop from the backend without the user experiencing input lag. ## Input Types The `type` prop controls how the input is rendered (e.g. plain text, password, file picker). It accepts the same values as the native HTML `` attribute, such as: - `"text"` (default) - `"password"` - `"email"` - `"number"` - `"file"` - `"checkbox"` - `"radio"` - `"date"` - `"time"` - `"url"` - `"color"` and several others. See the [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) for the full list. ```python demo rx.vstack( rx.input(placeholder="Username", type="text"), rx.input(placeholder="Password", type="password"), rx.input(type="date"), ) ``` ## Submitting a form using input The `name` prop is needed to submit with its owning form as part of a name/value pair. When the `required` prop is `True`, it indicates that the user must input text before the owning form can be submitted. The `type` is set here to `password`. The element is presented as a one-line plain text editor control in which the text is obscured so that it cannot be read. The `type` prop can take any value of `email`, `file`, `password`, `text` and several others. Learn more [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). ```python demo exec class FormInputState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): """Handle the form submit.""" self.form_data = form_data def form_input1(): return rx.card( rx.vstack( rx.heading("Example Form", as_="h2"), rx.form.root( rx.hstack( rx.input( name="input", placeholder="Enter text...", type="text", required=True, ), rx.button("Submit", type="submit"), width="100%", ), on_submit=FormInputState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.hstack( rx.heading("Results:", as_="h2"), rx.badge(FormInputState.form_data.to_string()), ), align_items="left", width="100%", ), width="50%", ) ``` To learn more about how to use forms in the [Form](/docs/library/forms/form) docs. ## Setting a value without using a State var Set the value of the specified reference element, without needing to link it up to a State var. This is an alternate way to modify the value of the `input`. ```python demo rx.hstack( rx.input(id="input1"), rx.button("Erase", on_click=rx.set_value("input1", "")), ) ``` ## API Reference ### rx.input Captures user input with an optional slot for buttons and icons. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `access_key` | str | - | Provides a hint for generating a keyboard shortcut for the current element. | | `auto_capitalize` | Literal["off", "none", "on", "sentences", "words", "characters"] | - | Controls whether and how text input is automatically capitalized as it is entered/edited by the user. | | `content_editable` | Literal["inherit", "plaintext-only"], bool | - | Indicates whether the element's content is editable. | | `context_menu` | str | - | Defines the ID of a element which will serve as the element's context menu. | | `dir` | str | - | Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left). | | `draggable` | bool | - | Defines whether the element can be dragged. | | `enter_key_hint` | Literal["enter", "done", "go", "next", "previous", "search", "send"] | - | Hints what media types the media element is able to play. | | `hidden` | bool | - | Defines whether the element is hidden. | | `input_mode` | Literal["none", "text", "tel", "url", "email", "numeric", "decimal", "search"] | - | Defines the type of the element. | | `item_prop` | str | - | Defines the name of the element for metadata purposes. | | `lang` | str | - | Defines the language used in the element. | | `role` | Literal["alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"] | - | Defines the role of the element. | | `slot` | str | - | Assigns a slot in a shadow DOM shadow tree to an element. | | `spell_check` | bool | - | Defines whether the element may be checked for spelling errors. | | `tab_index` | int | - | Defines the position of the current element in the tabbing order. | | `title` | str | - | Defines a tooltip for the element. | | `accept` | str | - | Accepted types of files when the input is file type. | | `alt` | str | - | Alternate text for input type="image". | | `auto_complete` | bool | - | Whether the input should have autocomplete enabled. | | `auto_focus` | bool | - | Automatically focuses the input when the page loads. | | `capture` | Literal["user", "environment"], bool | - | Captures media from the user (camera or microphone). | | `checked` | bool | - | Indicates whether the input is checked (for checkboxes and radio buttons). | | `default_checked` | bool | - | The initial value (for checkboxes and radio buttons). | | `default_value` | str | - | The value of the input when initially rendered. | | `disabled` | bool | - | Disables the input. | | `form` | str | - | Associates the input with a form (by id). | | `form_action` | str | - | URL to send the form data to (for type="submit" buttons). | | `form_enc_type` | str | - | How the form data should be encoded when submitting to the server (for type="submit" buttons). | | `form_method` | str | - | HTTP method to use for sending form data (for type="submit" buttons). | | `form_no_validate` | bool | - | Bypasses form validation when submitting (for type="submit" buttons). | | `form_target` | str | - | Specifies where to display the response after submitting the form (for type="submit" buttons). | | `list` | str | - | References a datalist for suggested options. | | `max` | str, int, float | - | Specifies the maximum value for the input. | | `max_length` | int | - | Specifies the maximum number of characters allowed in the input. | | `min_length` | int | - | Specifies the minimum number of characters required in the input. | | `min` | str, int, float | - | Specifies the minimum value for the input. | | `multiple` | bool | - | Indicates whether multiple values can be entered in an input of the type email or file. | | `name` | str | - | Name of the input, used when sending form data. | | `pattern` | str | - | Regex pattern the input's value must match to be valid. | | `placeholder` | str | - | Placeholder text in the input. | | `read_only` | bool | - | Indicates whether the input is read-only. | | `required` | bool | - | Indicates that the input is required. | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | Text field size "1" - "3". | | `src` | str | - | URL for image inputs. | | `step` | str, int, float | - | Specifies the legal number intervals for an input. | | `type` | str | - | Specifies the type of input. | | `value` | str, int, float | - | Value of the input. | | `variant` | Literal["classic", "surface", "soft"] | - | Variant of text field: "classic", "surface", "soft". | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | Override theme color for text field. | | `radius` | Literal["none", "small", "medium", "large", "full"] | - | Override theme radius for text field: "none", "small", "medium", "large", "full". | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_key_down` | Fired when a key is pressed down. | | `on_key_up` | Fired when a key is released. | | `on_change` | Fired when the value of the textarea changes. | # Radio Group Source: http://localhost:3000/docs/library/forms/radio-group.md --- components: - rx.radio_group - rx.radio_group.root - rx.radio_group.item HighLevelRadioGroup: | lambda **props: rx.radio_group(["1", "2", "3", "4", "5"], **props) RadioGroupRoot: | lambda **props: rx.radio_group.root( rx.radio_group.item(value="1"), rx.radio_group.item(value="2"), rx.radio_group.item(value="3"), rx.radio_group.item(value="4"), rx.radio_group.item(value="5"), **props ) RadioGroupItem: | lambda **props: rx.radio_group.root( rx.radio_group.item(value="1", **props), rx.radio_group.item(value="2", **props), rx.radio_group.item(value="3", **props), rx.radio_group.item(value="4", **props), rx.radio_group.item(value="5", **props), default_value="1", ) --- ```python exec import reflex as rx ``` A set of interactive radio buttons where only one can be selected at a time. ## Basic example ```python demo exec class RadioGroupState(rx.State): item: str = "No Selection" @rx.event def set_item(self, item: str): self.item = item def radio_group_state_example(): return rx.vstack( rx.badge(RadioGroupState.item, color_scheme="green"), rx.radio(["1", "2", "3"], on_change=RadioGroupState.set_item, direction="row"), ) ``` ## Submitting a form using Radio Group The `name` prop is used to name the group. It is submitted with its owning form as part of a name/value pair. When the `required` prop is `True`, it indicates that the user must check a radio item before the owning form can be submitted. ```python demo exec class FormRadioState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): """Handle the form submit.""" self.form_data = form_data def radio_form_example(): return rx.card( rx.vstack( rx.heading("Example Form", as_="h2"), rx.form.root( rx.vstack( rx.radio_group( ["Option 1", "Option 2", "Option 3"], name="radio_choice", direction="row", ), rx.button("Submit", type="submit"), width="100%", spacing="4", ), on_submit=FormRadioState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.hstack( rx.heading("Results:", as_="h2"), rx.badge(FormRadioState.form_data.to_string()), ), align_items="left", width="100%", spacing="4", ), width="50%", ) ``` ## API Reference ### rx.radio_group High level wrapper for the RadioGroup component. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `items` | Sequence[str] | - | The items of the radio group. | | `direction` | Literal["row", "column", "row-reverse", "column-reverse"] | - | The direction of the radio group. | | `spacing` | Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] | - | The gap between the items of the radio group. | | `size` | Literal["1", "2", "3"] | - | The size of the radio group. | | `variant` | Literal["classic", "surface", "soft"] | - | The variant of the radio group. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color of the radio group. | | `high_contrast` | bool | - | Whether to render the radio group with higher contrast color against background. | | `value` | str | - | The controlled value of the radio item to check. Should be used in conjunction with on_change. | | `default_value` | str | - | The initial value of checked radio item. Should be used in conjunction with on_change. | | `disabled` | bool | - | Whether the radio group is disabled. | | `name` | str | - | The name of the group. Submitted with its owning form as part of a name/value pair. | | `required` | bool | - | Whether the radio group is required. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ ### rx.radio_group.root A set of interactive radio buttons where only one can be selected at a time. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | Literal["1", "2", "3"], Breakpoints[str, Literal["1", "2", "3"]] | - | The size of the radio group: "1", "2", "3". | | `variant` | Literal["classic", "surface", "soft"] | - | The variant of the radio group. | | `color_scheme` | Literal["tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray"] | - | The color of the radio group. | | `high_contrast` | bool | - | Whether to render the radio group with higher contrast color against background. | | `value` | str | - | The controlled value of the radio item to check. Should be used in conjunction with on_change. | | `default_value` | str | - | The initial value of checked radio item. Should be used in conjunction with on_change. | | `disabled` | bool | - | Whether the radio group is disabled. | | `name` | str | - | The name of the group. Submitted with its owning form as part of a name/value pair. | | `required` | bool | - | Whether the radio group is required. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ Component-specific event triggers: | Event Trigger | Description | | --- | --- | | `on_change` | Fired when the value of the radio group changes. | ### rx.radio_group.item An item in the group that can be checked. #### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | str | - | The value of the radio item to check. Should be used in conjunction with on_change. | | `disabled` | bool | - | When true, prevents the user from interacting with the radio item. | | `required` | bool | - | When true, indicates that the user must check the radio item before the owning form can be submitted. | #### Event Triggers Base event triggers: https://reflex.dev/docs/api-reference/event-triggers/ # Select Source: http://localhost:3000/docs/library/forms/select.md --- components: - rx.select HighLevelSelect: | lambda **props: rx.select(["apple", "grape", "pear"], default_value="pear", **props) SelectRoot: | lambda **props: rx.select.root( rx.select.trigger(), rx.select.content( rx.select.group( rx.select.item("apple", value="apple"), rx.select.item("grape", value="grape"), rx.select.item("pear", value="pear"), ), ), default_value="pear", **props ) SelectTrigger: | lambda **props: rx.select.root( rx.select.trigger(**props), rx.select.content( rx.select.group( rx.select.item("apple", value="apple"), rx.select.item("grape", value="grape"), rx.select.item("pear", value="pear"), ), ), default_value="pear", ) SelectContent: | lambda **props: rx.select.root( rx.select.trigger(), rx.select.content( rx.select.group( rx.select.item("apple", value="apple"), rx.select.item("grape", value="grape"), rx.select.item("pear", value="pear"), ), **props, ), default_value="pear", ) SelectItem: | lambda **props: rx.select.root( rx.select.trigger(), rx.select.content( rx.select.group( rx.select.item("apple", value="apple", **props), rx.select.item("grape", value="grape", **props), rx.select.item("pear", value="pear", **props), ), ), default_value="pear", ) --- ```python exec import random import reflex as rx ``` A select component displays a dropdown list of options for the user to pick one from. It's one of the most common form controls in web applications, use it whenever you need a user to choose a single value from a predefined list and screen space is limited. Reflex's `rx.select` is a fully-featured, accessible dropdown built on Radix UI primitives. It works with simple option lists or full state-bound bindings, supports form integration, keyboard navigation, and works out-of-the-box on desktop and mobile. ## When to Use Select Use `rx.select` when: - You have more than five options and need to save vertical space - The user should choose exactly one option from a predefined list - You want a consistent dropdown that matches your app's theme Consider alternatives when: - You have fewer than five options → use [Radio Group](/docs/library/forms/radio-group) for better visibility - Users need to select multiple values → use [Checkbox](/docs/library/forms/checkbox) groups or a multi-select pattern - Users should type a value rather than choose one → use [Input](/docs/library/forms/input) - You need hierarchical or searchable options → use the [low-level Select API](/docs/library/forms/select/low) with custom content ## Basic Usage At its simplest, pass a list of string options. The component renders a dropdown button that opens a menu of choices. ```python demo rx.select(["apple", "grape", "pear"]) ``` The user can click the trigger button to open the dropdown and choose a different option. ## Tracking the Selected Value In most real apps, you need to react when the user selects a value, to filter data, update a chart, save to a database, or trigger another event. Bind the select to a [State](/docs/state/overview) var using the `value` prop and an `on_change` event handler. ```python demo exec class SelectState(rx.State): selected_fruit: str = "apple" @rx.event def update_fruit(self, value: str): self.selected_fruit = value def select_with_state(): return rx.hstack( rx.select( ["apple", "grape", "pear"], value=SelectState.selected_fruit, on_change=SelectState.update_fruit, ), rx.text("You selected:"), rx.badge(SelectState.selected_fruit), align="center", spacing="3", ) ``` This is the most common pattern. Because the select is bound to a state var, the value always reflects what's stored there, you can update it from anywhere in your app, and the select will re-render automatically. ## Setting a Default Value If you just need the select to start with a specific option chosen, without tracking the user's choice in state, use `default_value`. This is the simplest pattern when you only care about the value at form submission time, or when you don't need the selected value to affect anything else in your app. ```python demo rx.select( ["apple", "grape", "pear"], default_value="grape", ) ``` ## Placeholder Text When you want the select to start empty, prompting the user to make a choice, omit `default_value` and provide a `placeholder`. ```python demo rx.select( ["apple", "grape", "pear"], placeholder="Choose a fruit…", ) ``` ## Dynamic Options from State Real applications rarely have hardcoded options. More often, options come from a database, API, or calculated from other state. Pass a state var as the options list, and the dropdown updates whenever the list changes. ```python demo exec class SelectStateDynamic(rx.State): options: list[str] = ["apple", "grape", "pear"] selected: str = "apple" @rx.event def set_selected(self, value: str): self.selected = value @rx.event def randomize(self): self.selected = random.choice(self.options) @rx.event def add_option(self): new_options = ["banana", "orange", "mango", "kiwi", "cherry"] available = [o for o in new_options if o not in self.options] if available: self.options = self.options + [available[0]] def select_dynamic(): return rx.vstack( rx.select( SelectStateDynamic.options, value=SelectStateDynamic.selected, on_change=SelectStateDynamic.set_selected, ), rx.hstack( rx.button("Pick Random", on_click=SelectStateDynamic.randomize), rx.button("Add Option", on_click=SelectStateDynamic.add_option), spacing="2", ), spacing="3", ) ``` ## Disabled State To prevent user interaction, set `disabled=True`. The select renders with a muted appearance and cannot be opened. ```python demo rx.select( ["apple", "grape", "pear"], default_value="apple", disabled=True, ) ``` To disable individual items rather than the whole select, use the [low-level API](/docs/library/forms/select/low) and set `disabled=True` on specific `rx.select.item` components. ## Using Select in a Form Select components integrate cleanly with Reflex forms. The `name` prop sets the key used when the form is submitted, and `required=True` prevents submission until the user makes a choice. ```python demo exec class SelectFormState(rx.State): form_data: dict = {} @rx.event def handle_submit(self, form_data: dict): self.form_data = form_data def select_form(): return rx.card( rx.vstack( rx.heading("Order Form", as_="h2", size="4"), rx.form.root( rx.vstack( rx.text("Favorite fruit"), rx.select( ["apple", "grape", "pear"], name="fruit", placeholder="Pick one", required=True, ), rx.text("Quantity"), rx.select( ["1", "2", "3", "4", "5"], name="quantity", default_value="1", ), rx.button("Submit Order", type="submit"), spacing="2", align="stretch", ), on_submit=SelectFormState.handle_submit, reset_on_submit=True, ), rx.divider(), rx.hstack( rx.heading("Results:", as_="h2"), rx.badge(SelectFormState.form_data.to_string()), ), spacing="3", width="100%", align_items="left", ), width="400px", ) ``` For full details on building forms, validation, and submission handling, see the [Form documentation](/docs/library/forms/form). ## Mapping Display Labels to Underlying Values When your options have separate display labels and underlying values (e.g., a user ID for the value, a name for the label), use a [computed var](/docs/vars/computed-vars) to map between them. ```python demo exec class SelectDictState(rx.State): users: dict[str, str] = { "user_001": "Alice Johnson", "user_002": "Bob Smith", "user_003": "Carol Davis", } selected_name: str = "Alice Johnson" @rx.var def user_names(self) -> list[str]: return list(self.users.values()) @rx.var def selected_id(self) -> str: for uid, name in self.users.items(): if name == self.selected_name: return uid return "" @rx.event def set_user(self, name: str): self.selected_name = name def select_dict_example(): return rx.vstack( rx.select( SelectDictState.user_names, value=SelectDictState.selected_name, on_change=SelectDictState.set_user, ), rx.text("Selected user ID:"), rx.badge(SelectDictState.selected_id), spacing="3", ) ``` For native label/value separation in the dropdown itself, use the [low-level Select API](/docs/library/forms/select/low) and pass `value=` and a display label child to each `rx.select.item`. ## Using Select Inside a Dialog When placing a select inside a [Dialog](/docs/library/overlay/dialog) or other portal-based container, set `position="popper"` on the select so the dropdown menu positions itself correctly above the overlay content. ```python demo rx.dialog.root( rx.dialog.trigger(rx.button("Open Dialog")), rx.dialog.content( rx.dialog.title("Pick a fruit"), rx.vstack( rx.select( ["apple", "grape", "pear"], position="popper", default_value="apple", ), rx.dialog.close(rx.button("Close")), spacing="3", ), ), ) ``` ## React to Open and Close Events Beyond `on_change`, the `on_open_change` event fires when the dropdown opens or closes. Use this to trigger analytics, prefetch data, or animate related UI. The example below uses [rx.cond](/docs/library/dynamic-rendering/cond) to swap between two badges based on whether the dropdown is open. ```python demo exec class SelectOpenState(rx.State): is_open: bool = False open_count: int = 0 @rx.event def on_toggle(self, is_open: bool): self.is_open = is_open if is_open: self.open_count += 1 def select_open_change(): return rx.vstack( rx.select( ["apple", "grape", "pear"], default_value="apple", on_open_change=SelectOpenState.on_toggle, ), rx.text("Open count: ", SelectOpenState.open_count), rx.cond( SelectOpenState.is_open, rx.badge("Open", color_scheme="green"), rx.badge("Closed", color_scheme="gray"), ), spacing="3", ) ``` ## Common Patterns ### Filtering a List Based on Selection A classic use case, the select controls what data is displayed elsewhere on the page. ```python demo exec class FilterState(rx.State): all_items: list[dict[str, str]] = [ {"name": "MacBook Pro", "category": "laptops"}, {"name": "Dell XPS", "category": "laptops"}, {"name": "iPhone 15", "category": "phones"}, {"name": "Pixel 8", "category": "phones"}, {"name": "iPad Air", "category": "tablets"}, {"name": "Galaxy Tab", "category": "tablets"}, ] category: str = "laptops" @rx.event def set_category(self, value: str): self.category = value @rx.var def filtered_items(self) -> list[dict[str, str]]: return [i for i in self.all_items if i["category"] == self.category] def select_filter(): return rx.vstack( rx.select( ["laptops", "phones", "tablets"], value=FilterState.category, on_change=FilterState.set_category, ), rx.foreach( FilterState.filtered_items, lambda item: rx.card(item["name"]), ), spacing="3", width="300px", ) ``` ### Cascading Selects When one select's options depend on another's value, a common pattern for country/state, category/subcategory, etc. ```python demo exec class CascadeState(rx.State): regions: dict[str, list[str]] = { "North America": ["USA", "Canada", "Mexico"], "Europe": ["UK", "France", "Germany"], "Asia": ["Japan", "Korea", "Singapore"], } region: str = "North America" country: str = "USA" @rx.event def set_region(self, value: str): self.region = value self.country = self.regions[value][0] @rx.event def set_country(self, value: str): self.country = value @rx.var def region_names(self) -> list[str]: return list(self.regions.keys()) @rx.var def countries(self) -> list[str]: return self.regions.get(self.region, []) def select_cascade(): return rx.hstack( rx.select( CascadeState.region_names, value=CascadeState.region, on_change=CascadeState.set_region, ), rx.select( CascadeState.countries, value=CascadeState.country, on_change=CascadeState.set_country, ), spacing="3", ) ``` ## Frequently Asked Questions ### What is the best alternative to st.selectbox? For prototypes and quick data scripts, `st.selectbox` is fine. But for production apps, you need a dropdown component that doesn't trigger a full-page rerun on every interaction. `rx.select` is the most direct replacement, same API simplicity, but built on Radix UI as a real React component bound to Python state. It supports proper form integration, keyboard navigation, and accessibility out of the box, and works in apps with authentication, routing, and complex state. ### How do you make a dropdown menu in Python without writing JavaScript? Use `rx.select(["option_1", "option_2", "option_3"])`. That's the full code for a working dropdown, Reflex compiles your Python to a React app, so the menu renders as a styled, accessible web component without you writing any JavaScript, HTML, or CSS. For interactive dropdowns bound to your app state, add `value=` and `on_change=` props pointing to a state var and event handler. ### How do you replace dcc.Dropdown with a simpler API? Plotly Dash's `dcc.Dropdown` works but requires the callback decorator pattern, every interaction needs a `@app.callback` with explicit inputs and outputs. `rx.select` does the same job with standard Python event handlers: define a method on your state class, pass it to `on_change=`, and Reflex handles the wiring. Migration is usually mechanical: replace `dcc.Dropdown(options=..., value=..., id=...)` with `rx.select(options, value=State.value, on_change=State.set_value)` and delete the `@app.callback` decorators. ### Why does my Streamlit selectbox cause the whole page to rerun? Because that's how Streamlit works by default, every widget interaction reruns your entire script from the top. This is fine for small scripts but becomes a performance problem as your app grows. The alternatives are: use `st.cache_data` and `st.session_state` aggressively to limit recomputation, switch to fragments (`@st.fragment`) for isolated reruns, or move to a framework like Reflex where state updates trigger granular re-renders instead of full reruns. Reflex's `rx.select` only updates the UI elements that actually depend on the selected value. ### What dropdown component works best for production Python web apps? For prototypes, `st.selectbox` (Streamlit), `gr.Dropdown` (Gradio), and `dcc.Dropdown` (Plotly Dash) all work fine. For production apps with auth, routing, and complex state, `rx.select` (Reflex) is purpose-built, it's a real React component under the hood, supports proper form integration, and doesn't suffer from the full-page rerun problem that limits Streamlit at scale. The choice often comes down to whether you're building a quick demo or a real product. ### How do you build a dropdown that filters a table of data? Bind the dropdown to a state variable, then derive the filtered data using a computed property. In Reflex: `rx.select(categories, value=State.selected, on_change=State.set_selected)` paired with a `@rx.var filtered_rows(self) -> list: return [r for r in self.all_rows if r.category == self.selected]`. The pattern is essentially the same in Streamlit, Dash, and Gradio, but Reflex's version only re-renders the table, not the entire page. ### What's a good Retool alternative for building admin panels with dropdowns? Reflex is the most popular open-source alternative to Retool for building internal tools and admin panels. Unlike Retool, which is a hosted low-code platform with seat-based pricing, Reflex is open source, self-hostable, and lets you build everything in Python, including dropdowns (`rx.select`), tables, forms, and complete CRUD workflows. You get the same component-based UX as Retool, but with full code ownership and no per-user fees. ### How do you populate a dropdown from a database in Python? Load the values into a state variable when the page loads, then pass the variable as the options list. In a Reflex app: define `options: list[str] = []` on a State class, populate it in an `on_mount` handler that queries your database, and pass `State.options` to `rx.select`. The dropdown updates automatically whenever the underlying data changes. The same pattern works with any database, SQLAlchemy, raw SQL, an ORM, or an API call. ### How do you build a dropdown with searchable or filterable options? Native `` vs `