├── .devcontainer └── devcontainer.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── build_issue.md │ ├── cloud_issue.md │ ├── custom_component_request.md │ ├── enhancement_request.md │ ├── enterprise_issue.md │ └── feature_request.md ├── actions │ └── setup_build_env │ │ └── action.yml ├── codeql-config.yml ├── pull_request_template.md └── workflows │ ├── check_node_latest.yml │ ├── check_outdated_dependencies.yml │ ├── codeql.yml │ ├── dependency-review.yml │ ├── integration_app_harness.yml │ ├── integration_tests.yml │ ├── performance.yml │ ├── pre-commit.yml │ ├── reflex_init_in_docker_test.yml │ └── unit_tests.yml ├── .gitignore ├── .python-version ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docker-example ├── README.md ├── production-app-platform │ ├── .dockerignore │ ├── Dockerfile │ └── README.md ├── production-compose │ ├── .dockerignore │ ├── Caddy.Dockerfile │ ├── Caddyfile │ ├── Dockerfile │ ├── README.md │ ├── compose.prod.yaml │ ├── compose.tools.yaml │ └── compose.yaml ├── production-one-port │ ├── .dockerignore │ ├── Caddyfile │ ├── Dockerfile │ └── README.md ├── simple-one-port │ ├── .dockerignore │ ├── Caddyfile │ ├── Dockerfile │ └── README.md └── simple-two-port │ ├── .dockerignore │ ├── Dockerfile │ └── README.md ├── docs ├── DEBUGGING.md ├── de │ └── README.md ├── es │ └── README.md ├── images │ ├── dalle.gif │ ├── dalle_colored_code_example.png │ ├── reflex.png │ ├── reflex.svg │ ├── reflex_dark.svg │ └── reflex_light.svg ├── in │ └── README.md ├── it │ └── README.md ├── ja │ └── README.md ├── kr │ └── README.md ├── pe │ └── README.md ├── pt │ └── pt_br │ │ └── README.md ├── tr │ └── README.md ├── vi │ └── README.md └── zh │ ├── zh_cn │ └── README.md │ └── zh_tw │ └── README.md ├── pyi_hashes.json ├── pyproject.toml ├── reflex ├── .templates │ ├── apps │ │ └── blank │ │ │ ├── assets │ │ │ └── favicon.ico │ │ │ └── code │ │ │ ├── __init__.py │ │ │ └── blank.py │ ├── jinja │ │ ├── app │ │ │ └── rxconfig.py.jinja2 │ │ ├── custom_components │ │ │ ├── README.md.jinja2 │ │ │ ├── __init__.py.jinja2 │ │ │ ├── demo_app.py.jinja2 │ │ │ ├── pyproject.toml.jinja2 │ │ │ └── src.py.jinja2 │ │ └── web │ │ │ ├── package.json.jinja2 │ │ │ ├── pages │ │ │ ├── _app.js.jinja2 │ │ │ ├── _document.js.jinja2 │ │ │ ├── base_page.js.jinja2 │ │ │ ├── component.js.jinja2 │ │ │ ├── custom_component.js.jinja2 │ │ │ ├── index.js.jinja2 │ │ │ ├── macros.js.jinja2 │ │ │ ├── stateful_component.js.jinja2 │ │ │ ├── stateful_components.js.jinja2 │ │ │ └── utils.js.jinja2 │ │ │ ├── styles │ │ │ └── styles.css.jinja2 │ │ │ └── utils │ │ │ ├── context.js.jinja2 │ │ │ └── theme.js.jinja2 │ └── web │ │ ├── .gitignore │ │ ├── components │ │ ├── reflex │ │ │ └── radix_themes_color_mode_provider.js │ │ └── shiki │ │ │ └── code.js │ │ ├── jsconfig.json │ │ ├── next.config.js │ │ ├── postcss.config.js │ │ └── utils │ │ ├── client_side_routing.js │ │ ├── helpers │ │ ├── dataeditor.js │ │ ├── debounce.js │ │ ├── paste.js │ │ ├── range.js │ │ └── throttle.js │ │ └── state.js ├── __init__.py ├── __main__.py ├── admin.py ├── app.py ├── app_mixins │ ├── __init__.py │ ├── lifespan.py │ ├── middleware.py │ └── mixin.py ├── assets.py ├── base.py ├── compiler │ ├── __init__.py │ ├── compiler.py │ ├── templates.py │ └── utils.py ├── components │ ├── __init__.py │ ├── base │ │ ├── __init__.py │ │ ├── app_wrap.py │ │ ├── bare.py │ │ ├── body.py │ │ ├── document.py │ │ ├── error_boundary.py │ │ ├── fragment.py │ │ ├── head.py │ │ ├── link.py │ │ ├── meta.py │ │ ├── script.py │ │ └── strict_mode.py │ ├── component.py │ ├── core │ │ ├── __init__.py │ │ ├── auto_scroll.py │ │ ├── banner.py │ │ ├── breakpoints.py │ │ ├── client_side_routing.py │ │ ├── clipboard.py │ │ ├── colors.py │ │ ├── cond.py │ │ ├── debounce.py │ │ ├── foreach.py │ │ ├── html.py │ │ ├── layout │ │ │ └── __init__.py │ │ ├── match.py │ │ ├── responsive.py │ │ ├── sticky.py │ │ └── upload.py │ ├── datadisplay │ │ ├── __init__.py │ │ ├── code.py │ │ ├── dataeditor.py │ │ ├── logo.py │ │ └── shiki_code_block.py │ ├── dynamic.py │ ├── el │ │ ├── __init__.py │ │ ├── constants │ │ │ ├── __init__.py │ │ │ ├── html.py │ │ │ ├── react.py │ │ │ └── reflex.py │ │ ├── element.py │ │ └── elements │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── forms.py │ │ │ ├── inline.py │ │ │ ├── media.py │ │ │ ├── metadata.py │ │ │ ├── other.py │ │ │ ├── scripts.py │ │ │ ├── sectioning.py │ │ │ ├── tables.py │ │ │ └── typography.py │ ├── gridjs │ │ ├── __init__.py │ │ └── datatable.py │ ├── literals.py │ ├── lucide │ │ ├── __init__.py │ │ └── icon.py │ ├── markdown │ │ ├── __init__.py │ │ └── markdown.py │ ├── moment │ │ ├── __init__.py │ │ └── moment.py │ ├── next │ │ ├── __init__.py │ │ ├── base.py │ │ ├── image.py │ │ ├── link.py │ │ └── video.py │ ├── plotly │ │ ├── __init__.py │ │ └── plotly.py │ ├── props.py │ ├── radix │ │ ├── __init__.py │ │ ├── primitives │ │ │ ├── __init__.py │ │ │ ├── accordion.py │ │ │ ├── base.py │ │ │ ├── drawer.py │ │ │ ├── form.py │ │ │ ├── progress.py │ │ │ └── slider.py │ │ └── themes │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── color_mode.py │ │ │ ├── components │ │ │ ├── __init__.py │ │ │ ├── alert_dialog.py │ │ │ ├── aspect_ratio.py │ │ │ ├── avatar.py │ │ │ ├── badge.py │ │ │ ├── button.py │ │ │ ├── callout.py │ │ │ ├── card.py │ │ │ ├── checkbox.py │ │ │ ├── checkbox_cards.py │ │ │ ├── checkbox_group.py │ │ │ ├── context_menu.py │ │ │ ├── data_list.py │ │ │ ├── dialog.py │ │ │ ├── dropdown_menu.py │ │ │ ├── hover_card.py │ │ │ ├── icon_button.py │ │ │ ├── inset.py │ │ │ ├── popover.py │ │ │ ├── progress.py │ │ │ ├── radio.py │ │ │ ├── radio_cards.py │ │ │ ├── radio_group.py │ │ │ ├── scroll_area.py │ │ │ ├── segmented_control.py │ │ │ ├── select.py │ │ │ ├── separator.py │ │ │ ├── skeleton.py │ │ │ ├── slider.py │ │ │ ├── spinner.py │ │ │ ├── switch.py │ │ │ ├── table.py │ │ │ ├── tabs.py │ │ │ ├── text_area.py │ │ │ ├── text_field.py │ │ │ └── tooltip.py │ │ │ ├── layout │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── box.py │ │ │ ├── center.py │ │ │ ├── container.py │ │ │ ├── flex.py │ │ │ ├── grid.py │ │ │ ├── list.py │ │ │ ├── section.py │ │ │ ├── spacer.py │ │ │ └── stack.py │ │ │ └── typography │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── blockquote.py │ │ │ ├── code.py │ │ │ ├── heading.py │ │ │ ├── link.py │ │ │ └── text.py │ ├── react_player │ │ ├── __init__.py │ │ ├── audio.py │ │ ├── react_player.py │ │ └── video.py │ ├── recharts │ │ ├── __init__.py │ │ ├── cartesian.py │ │ ├── charts.py │ │ ├── general.py │ │ ├── polar.py │ │ └── recharts.py │ ├── sonner │ │ ├── __init__.py │ │ └── toast.py │ ├── suneditor │ │ ├── __init__.py │ │ └── editor.py │ └── tags │ │ ├── __init__.py │ │ ├── cond_tag.py │ │ ├── iter_tag.py │ │ ├── match_tag.py │ │ ├── tag.py │ │ └── tagless.py ├── config.py ├── constants │ ├── __init__.py │ ├── base.py │ ├── colors.py │ ├── compiler.py │ ├── config.py │ ├── custom_components.py │ ├── event.py │ ├── installer.py │ ├── route.py │ ├── state.py │ └── utils.py ├── custom_components │ ├── __init__.py │ └── custom_components.py ├── environment.py ├── event.py ├── experimental │ ├── __init__.py │ ├── client_state.py │ └── hooks.py ├── istate │ ├── __init__.py │ ├── data.py │ ├── dynamic.py │ ├── manager.py │ ├── proxy.py │ ├── storage.py │ └── wrappers.py ├── middleware │ ├── __init__.py │ ├── hydrate_middleware.py │ └── middleware.py ├── model.py ├── page.py ├── plugins │ ├── __init__.py │ ├── base.py │ ├── tailwind_v3.py │ └── tailwind_v4.py ├── py.typed ├── reflex.py ├── route.py ├── state.py ├── style.py ├── testing.py ├── utils │ ├── __init__.py │ ├── build.py │ ├── codespaces.py │ ├── compat.py │ ├── console.py │ ├── decorator.py │ ├── exceptions.py │ ├── exec.py │ ├── export.py │ ├── format.py │ ├── imports.py │ ├── lazy_loader.py │ ├── misc.py │ ├── net.py │ ├── path_ops.py │ ├── prerequisites.py │ ├── processes.py │ ├── pyi_generator.py │ ├── redir.py │ ├── registry.py │ ├── serializers.py │ ├── telemetry.py │ └── types.py └── vars │ ├── __init__.py │ ├── base.py │ ├── datetime.py │ ├── dep_tracking.py │ ├── function.py │ ├── number.py │ ├── object.py │ └── sequence.py ├── scripts ├── __init__.py ├── bun_install.sh ├── darglint_test.bat ├── hatch_build.py ├── install.ps1 ├── integration.sh ├── make_pyi.py └── wait_for_listening_port.py ├── tests ├── __init__.py ├── benchmarks │ ├── __init__.py │ ├── conftest.py │ ├── fixtures.py │ ├── test_compilation.py │ └── test_evaluate.py ├── integration │ ├── __init__.py │ ├── conftest.py │ ├── init-test │ │ ├── Dockerfile │ │ └── in_docker_test_script.sh │ ├── shared │ │ └── state.py │ ├── test_background_task.py │ ├── test_call_script.py │ ├── test_client_storage.py │ ├── test_component_state.py │ ├── test_computed_vars.py │ ├── test_connection_banner.py │ ├── test_deploy_url.py │ ├── test_dynamic_components.py │ ├── test_dynamic_routes.py │ ├── test_event_actions.py │ ├── test_event_chain.py │ ├── test_exception_handlers.py │ ├── test_extra_overlay_function.py │ ├── test_form_submit.py │ ├── test_icon.py │ ├── test_input.py │ ├── test_large_state.py │ ├── test_lifespan.py │ ├── test_login_flow.py │ ├── test_media.py │ ├── test_memo.py │ ├── test_navigation.py │ ├── test_server_side_event.py │ ├── test_shared_state.py │ ├── test_state_inheritance.py │ ├── test_tailwind.py │ ├── test_upload.py │ ├── test_urls.py │ ├── test_var_operations.py │ ├── tests_playwright │ │ ├── test_appearance.py │ │ ├── test_datetime_operations.py │ │ ├── test_link_hover.py │ │ ├── test_stateless_app.py │ │ └── test_table.py │ └── utils.py ├── test_node_version.py └── units │ ├── __init__.py │ ├── assets │ ├── custom_script.js │ └── test_assets.py │ ├── compiler │ ├── __init__.py │ ├── test_compiler.py │ └── test_compiler_utils.py │ ├── components │ ├── __init__.py │ ├── base │ │ ├── test_bare.py │ │ ├── test_link.py │ │ └── test_script.py │ ├── core │ │ ├── __init__.py │ │ ├── test_banner.py │ │ ├── test_colors.py │ │ ├── test_cond.py │ │ ├── test_debounce.py │ │ ├── test_foreach.py │ │ ├── test_html.py │ │ ├── test_match.py │ │ ├── test_responsive.py │ │ └── test_upload.py │ ├── datadisplay │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_code.py │ │ ├── test_dataeditor.py │ │ ├── test_datatable.py │ │ └── test_shiki_code.py │ ├── el │ │ ├── test_html.py │ │ └── test_svg.py │ ├── forms │ │ ├── __init__.py │ │ └── test_form.py │ ├── graphing │ │ ├── __init__.py │ │ ├── test_plotly.py │ │ └── test_recharts.py │ ├── layout │ │ └── __init__.py │ ├── lucide │ │ └── test_icon.py │ ├── markdown │ │ ├── __init__.py │ │ └── test_markdown.py │ ├── media │ │ ├── __init__.py │ │ └── test_image.py │ ├── radix │ │ ├── test_icon_button.py │ │ └── test_layout.py │ ├── recharts │ │ ├── test_cartesian.py │ │ └── test_polar.py │ ├── test_component.py │ ├── test_component_future_annotations.py │ ├── test_component_state.py │ ├── test_props.py │ ├── test_tag.py │ └── typography │ │ ├── __init__.py │ │ └── test_markdown.py │ ├── conftest.py │ ├── middleware │ ├── __init__.py │ ├── conftest.py │ └── test_hydrate_middleware.py │ ├── states │ ├── __init__.py │ ├── mutation.py │ └── upload.py │ ├── test_app.py │ ├── test_attribute_access_type.py │ ├── test_base.py │ ├── test_config.py │ ├── test_db_config.py │ ├── test_event.py │ ├── test_health_endpoint.py │ ├── test_model.py │ ├── test_page.py │ ├── test_prerequisites.py │ ├── test_route.py │ ├── test_sqlalchemy.py │ ├── test_state.py │ ├── test_state_tree.py │ ├── test_style.py │ ├── test_telemetry.py │ ├── test_testing.py │ ├── test_var.py │ ├── utils │ ├── __init__.py │ ├── test_format.py │ ├── test_imports.py │ ├── test_serializers.py │ ├── test_types.py │ └── test_utils.py │ └── vars │ ├── test_base.py │ └── test_object.py └── uv.lock /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bookworm", 3 | "postCreateCommand": "/bin/bash -c 'python -m pip install uv && python -m uv sync & git clone https://github.com/reflex-dev/reflex-examples; wait'", 4 | "forwardPorts": [3000, 8000], 5 | "portsAttributes": { 6 | "3000": { 7 | "label": "Frontend", 8 | "onAutoForward": "notify" 9 | }, 10 | "8000": { 11 | "label": "Backend" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @reflex-dev/reflex-team 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | assignees: "" 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | Steps to reproduce the behavior: 13 | 14 | - Code/Link to Repo: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Specifics (please complete the following information):** 23 | 24 | - Python Version: 25 | - Reflex Version: 26 | - OS: 27 | - Browser (Optional): 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/build_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build Issue 3 | about: Report an issue related to reflex.build 4 | title: "[BUILD] " # This acts as a hint, but users can change it. 5 | labels: build 6 | assignees: "" 7 | --- 8 | 9 | ## Describe the issue 10 | 11 | Provide details about the issue. 12 | 13 | ... 14 | 15 | ## Expected behavior 16 | 17 | What should have happened? 18 | 19 | ... 20 | 21 | ## Steps to reproduce (if applicable) 22 | 23 | 1. 24 | 2. 25 | 3. 26 | 27 | ## Environment 28 | 29 | - Reflex Version: 30 | - Python Version: 31 | - OS: 32 | - Browser: 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/cloud_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Cloud Issue 3 | about: Report an issue related to Reflex Cloud 4 | title: "[CLOUD] " # This acts as a hint, but users can change it. 5 | labels: cloud 6 | assignees: "" 7 | --- 8 | 9 | ## Describe the issue 10 | 11 | Provide details about the issue. 12 | 13 | ... 14 | 15 | ## Expected behavior 16 | 17 | What should have happened? 18 | 19 | ... 20 | 21 | ## Steps to reproduce (if applicable) 22 | 23 | 1. 24 | 2. 25 | 3. 26 | 27 | ## Environment 28 | 29 | - Reflex Version: 30 | - Python Version: 31 | - OS: 32 | - Browser: 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom_component_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom Component Request 3 | about: Suggest a new custom component for Reflex 4 | title: "" 5 | labels: "custom component request" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the Custom Component** 10 | A clear and concise description of what the custom component does. 11 | 12 | - What is the purpose of the custom component? 13 | 14 | - What is the expected behavior of the custom component? 15 | 16 | - What are the use cases for the custom component? 17 | 18 | **Specifics (please complete the following information):** 19 | 20 | - Do you have a specific react package in mind? (Optional): 21 | 22 | **Additional context** 23 | Add any other context about the custom component here. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Request 3 | about: Suggest an enhancement for an existing Reflex feature. 4 | title: "" 5 | labels: "enhancement" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the Enhancement you want** 10 | A clear and concise description of what the improvement does. 11 | 12 | - Which feature do you want to improve? (and what problem does it have) 13 | 14 | - What is the benefit of the enhancement? 15 | 16 | - Show an example/usecase were the improvement are needed. 17 | 18 | **Additional context** 19 | Add any other context here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enterprise_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enterprise Issue 3 | about: Report an issue related to Reflex Enterprise 4 | title: "[ENTERPRISE] " # This acts as a hint, but users can change it. 5 | labels: enterprise 6 | assignees: "" 7 | --- 8 | 9 | ## Describe the issue 10 | 11 | Provide details about the issue. 12 | 13 | ... 14 | 15 | ## Expected behavior 16 | 17 | What should have happened? 18 | 19 | ... 20 | 21 | ## Steps to reproduce (if applicable) 22 | 23 | 1. 24 | 2. 25 | 3. 26 | 27 | ## Environment 28 | 29 | - Reflex Version: 30 | - Python Version: 31 | - OS: 32 | - Browser: 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest a new feature for Reflex 4 | title: "" 5 | labels: "feature request" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the Features** 10 | A clear and concise description of what the features does. 11 | 12 | - What is the purpose of the feature? 13 | 14 | - Show an example / use cases for the new feature. 15 | 16 | **Additional context** 17 | Add any other context here. 18 | -------------------------------------------------------------------------------- /.github/actions/setup_build_env/action.yml: -------------------------------------------------------------------------------- 1 | # Entry conditions: 2 | # - `setup/checkout` has already happened 3 | # - working dir is the root directory of your project (e.g. `reflex/`). 4 | # - You have a `uv.lock` file in the root directory of your project 5 | # - You have a `pyproject.toml` file in the root directory of your project 6 | # 7 | # Exit conditions: 8 | # - Python of version `python-version` is ready to be invoked as `python`. 9 | # - If `run-uv-sync` is true, deps as defined in `pyproject.toml` will have been installed into the venv at `create-venv-at-path`. 10 | 11 | name: "Setup Reflex build environment" 12 | description: "Sets up Python, install uv (cached), install project deps (cached)" 13 | inputs: 14 | python-version: 15 | description: "Python version setup" 16 | required: true 17 | run-uv-sync: 18 | description: "Whether to run uv sync on current dir" 19 | required: false 20 | default: false 21 | create-venv-at-path: 22 | description: "Path to venv (if uv sync is enabled)" 23 | required: false 24 | default: ".venv" 25 | 26 | runs: 27 | using: "composite" 28 | steps: 29 | - name: Install UV 30 | uses: astral-sh/setup-uv@v6 31 | with: 32 | python-version: ${{ inputs.python-version }} 33 | enable-cache: true 34 | prune-cache: false 35 | activate-environment: true 36 | cache-dependency-glob: "uv.lock" 37 | - name: Install Dependencies 38 | if: inputs.run-uv-sync == 'true' 39 | run: uv sync 40 | shell: bash 41 | -------------------------------------------------------------------------------- /.github/codeql-config.yml: -------------------------------------------------------------------------------- 1 | paths-ignore: 2 | - "**/tests/**" 3 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### All Submissions: 2 | 3 | - [ ] Have you followed the guidelines stated in [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) file? 4 | - [ ] Have you checked to ensure there aren't any other open [Pull Requests](https://github.com/reflex-dev/reflex/pulls) for the desired changed? 5 | 6 | 7 | 8 | ### Type of change 9 | 10 | Please delete options that are not relevant. 11 | 12 | - [ ] Bug fix (non-breaking change which fixes an issue) 13 | - [ ] New feature (non-breaking change which adds functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 15 | - [ ] This change requires a documentation update 16 | 17 | ### New Feature Submission: 18 | 19 | - [ ] Does your submission pass the tests? 20 | - [ ] Have you linted your code locally prior to submission? 21 | 22 | ### Changes To Core Features: 23 | 24 | - [ ] Have you added an explanation of what your changes do and why you'd like us to include them? 25 | - [ ] Have you written new tests for your core changes, as applicable? 26 | - [ ] Have you successfully ran tests with your changes locally? 27 | 28 | ### **After** these steps, you're ready to open a pull request. 29 | 30 | a. Give a descriptive title to your PR. 31 | 32 | b. Describe your changes. 33 | 34 | c. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). 35 | -------------------------------------------------------------------------------- /.github/workflows/check_node_latest.yml: -------------------------------------------------------------------------------- 1 | name: integration-node-latest 2 | permissions: 3 | contents: read 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | env: 14 | TELEMETRY_ENABLED: false 15 | 16 | jobs: 17 | check_latest_node: 18 | runs-on: ubuntu-22.04 19 | strategy: 20 | matrix: 21 | split_index: [1, 2] 22 | node-version: ["node"] 23 | fail-fast: false 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: ./.github/actions/setup_build_env 28 | with: 29 | python-version: 3.13 30 | run-uv-sync: true 31 | 32 | - uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{ matrix.node-version }} 35 | - run: uv run playwright install --with-deps 36 | - run: | 37 | uv run pytest tests/test_node_version.py 38 | uv run pytest tests/integration --splits 2 --group ${{matrix.split_index}} 39 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: "Dependency Review" 2 | on: [pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependency-review: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: "Checkout Repository" 12 | uses: actions/checkout@v4 13 | - name: "Dependency Review" 14 | uses: actions/dependency-review-action@v4 15 | with: 16 | allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, HPND, ISC, MIT, MPL-2.0, Unlicense, Python-2.0, Python-2.0.1, Apache-2.0 AND MIT, BSD-2-Clause AND BSD-3-Clause, Apache-2.0 AND BSD-3-Clause 17 | allow-dependencies-licenses: "pkg:pypi/lazy-loader" 18 | -------------------------------------------------------------------------------- /.github/workflows/integration_app_harness.yml: -------------------------------------------------------------------------------- 1 | name: integration-app-harness 2 | 3 | concurrency: 4 | group: ${{ github.workflow }}-${{ github.event.pull_request.id }} 5 | cancel-in-progress: true 6 | 7 | on: 8 | push: 9 | branches: ["main"] 10 | paths-ignore: 11 | - "**/*.md" 12 | pull_request: 13 | branches: ["main"] 14 | paths-ignore: 15 | - "**/*.md" 16 | env: 17 | APP_HARNESS_HEADLESS: 1 18 | 19 | permissions: 20 | contents: read 21 | 22 | jobs: 23 | integration-app-harness: 24 | timeout-minutes: 30 25 | strategy: 26 | matrix: 27 | state_manager: ["redis", "memory"] 28 | python-version: ["3.11", "3.12", "3.13"] 29 | split_index: [1, 2] 30 | fail-fast: false 31 | runs-on: ubuntu-22.04 32 | services: 33 | # Label used to access the service container 34 | redis: 35 | image: ${{ matrix.state_manager == 'redis' && 'redis' || '' }} 36 | # Set health checks to wait until redis has started 37 | options: >- 38 | --health-cmd "redis-cli ping" 39 | --health-interval 10s 40 | --health-timeout 5s 41 | --health-retries 5 42 | ports: 43 | # Maps port 6379 on service container to the host 44 | - 6379:6379 45 | steps: 46 | - uses: actions/checkout@v4 47 | - uses: ./.github/actions/setup_build_env 48 | with: 49 | python-version: ${{ matrix.python-version }} 50 | run-uv-sync: true 51 | 52 | - name: Run app harness tests 53 | env: 54 | REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} 55 | run: | 56 | uv run playwright install chromium 57 | uv run pytest tests/integration --retries 3 --maxfail=5 --splits 2 --group ${{matrix.split_index}} 58 | -------------------------------------------------------------------------------- /.github/workflows/performance.yml: -------------------------------------------------------------------------------- 1 | name: performance-tests 2 | permissions: 3 | contents: read 4 | 5 | on: 6 | push: 7 | branches: 8 | - "main" # or "master" 9 | paths-ignore: 10 | - "**/*.md" 11 | pull_request: 12 | workflow_dispatch: 13 | 14 | env: 15 | TELEMETRY_ENABLED: false 16 | NODE_OPTIONS: "--max_old_space_size=8192" 17 | PR_TITLE: ${{ github.event.pull_request.title }} 18 | APP_HARNESS_HEADLESS: 1 19 | PYTHONUNBUFFERED: 1 20 | 21 | jobs: 22 | benchmarks: 23 | name: Run benchmarks 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: Install uv 29 | uses: astral-sh/setup-uv@v6 30 | 31 | - name: Set up Python 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version: "3.13" 35 | 36 | - name: Install dependencies 37 | run: uv sync --all-extras --dev 38 | 39 | - name: Run benchmarks 40 | uses: CodSpeedHQ/action@v3 41 | with: 42 | token: ${{ secrets.CODSPEED_TOKEN }} 43 | run: uv run pytest tests/benchmarks --codspeed 44 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | permissions: 3 | contents: read 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.event.pull_request.id }} 7 | cancel-in-progress: true 8 | 9 | on: 10 | pull_request: 11 | branches: ["main"] 12 | push: 13 | # Note even though this job is called "pre-commit" and runs "pre-commit", this job will run 14 | # also POST-commit on main also! In case there are mishandled merge conflicts / bad auto-resolves 15 | # when merging into main branch. 16 | branches: ["main"] 17 | 18 | jobs: 19 | pre-commit: 20 | timeout-minutes: 30 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: ./.github/actions/setup_build_env 25 | with: 26 | python-version: 3.13 27 | run-uv-sync: true 28 | - uses: actions/checkout@v4 29 | with: 30 | clean: false 31 | - run: uv run pre-commit run --all-files --show-diff-on-failure 32 | -------------------------------------------------------------------------------- /.github/workflows/reflex_init_in_docker_test.yml: -------------------------------------------------------------------------------- 1 | name: reflex-init-in-docker-test 2 | permissions: 3 | contents: read 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.event.pull_request.id }} 7 | cancel-in-progress: true 8 | 9 | on: 10 | push: 11 | branches: ["main"] 12 | paths-ignore: 13 | - "**/*.md" 14 | pull_request: 15 | branches: ["main"] 16 | paths-ignore: 17 | - "**/*.md" 18 | 19 | jobs: 20 | # TODO we can extend to various starting points (e.g. Ubuntu with node, without node, with unzip, without unzip, etc.) 21 | # Currently starting point is: Ubuntu + unzip, xz-utils, Python suite. No node. 22 | reflex-install-and-init: 23 | timeout-minutes: 30 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - shell: bash 29 | run: | 30 | # Run reflex init in a docker container 31 | 32 | # cwd is repo root 33 | docker build -f tests/integration/init-test/Dockerfile -t reflex-init-test tests/integration/init-test 34 | docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/*.pyc 3 | assets/external/* 4 | dist/* 5 | examples/ 6 | .web 7 | .states 8 | .idea 9 | .vscode 10 | .coverage 11 | .coverage.* 12 | .venv 13 | venv 14 | requirements.txt 15 | .pyi_generator_last_run 16 | .pyi_generator_diff 17 | reflex.db 18 | .codspeed 19 | .env 20 | .env.* 21 | node_modules 22 | package-lock.json 23 | *.pyi 24 | .pre-commit-config.yaml -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | -------- | ------------------ | 7 | | >= 0.7.0 | :white_check_mark: | 8 | 9 | ## Reporting a Vulnerability 10 | 11 | Please report any security vulnerabilities to contact@reflex.dev 12 | -------------------------------------------------------------------------------- /docker-example/README.md: -------------------------------------------------------------------------------- 1 | # Reflex Docker Examples 2 | 3 | This directory contains several examples of how to deploy Reflex apps using docker. 4 | 5 | In all cases, ensure that your `requirements.txt` file is up to date and 6 | includes the `reflex` package. 7 | 8 | ## `simple-two-port` 9 | 10 | The most basic production deployment exposes two HTTP ports and relies on an 11 | existing load balancer to forward the traffic appropriately. 12 | 13 | ## `simple-one-port` 14 | 15 | This deployment exports the frontend statically and serves it via a single HTTP 16 | port using Caddy. This is useful for platforms that only support a single port 17 | or where running a node server in the container is undesirable. 18 | 19 | ## `production-compose` 20 | 21 | This deployment is intended for use with a standalone VPS that is only hosting a 22 | single Reflex app. It provides the entire stack in a single `compose.yaml` 23 | including a webserver, one or more backend instances, redis, and a postgres 24 | database. 25 | 26 | ## `production-app-platform` 27 | 28 | This example deployment is intended for use with App hosting platforms, like 29 | Azure, AWS, or Google Cloud Run. It is the backend of the deployment, which 30 | depends on a separately hosted redis instance and static frontend deployment. 31 | -------------------------------------------------------------------------------- /docker-example/production-app-platform/.dockerignore: -------------------------------------------------------------------------------- 1 | .web 2 | .git 3 | __pycache__/* 4 | Dockerfile 5 | uploaded_files 6 | -------------------------------------------------------------------------------- /docker-example/production-compose/.dockerignore: -------------------------------------------------------------------------------- 1 | .web 2 | .git 3 | __pycache__/* 4 | Dockerfile 5 | Caddy.Dockerfile 6 | compose.yaml 7 | compose.*.yaml 8 | uploaded_files 9 | -------------------------------------------------------------------------------- /docker-example/production-compose/Caddy.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM library/caddy 2 | 3 | COPY --from=local/reflex-app /app/.web/_static /srv 4 | ADD Caddyfile /etc/caddy/Caddyfile -------------------------------------------------------------------------------- /docker-example/production-compose/Caddyfile: -------------------------------------------------------------------------------- 1 | {$DOMAIN} 2 | 3 | encode gzip 4 | 5 | @backend_routes path /_event/* /ping /_upload /_upload/* 6 | handle @backend_routes { 7 | reverse_proxy app:8000 8 | } 9 | 10 | root * /srv 11 | route { 12 | try_files {path} {path}/ /404.html 13 | file_server 14 | } 15 | -------------------------------------------------------------------------------- /docker-example/production-compose/Dockerfile: -------------------------------------------------------------------------------- 1 | # This docker file is intended to be used with docker compose to deploy a production 2 | # instance of a Reflex app. 3 | 4 | # Stage 1: init 5 | FROM python:3.13 as init 6 | 7 | ARG uv=/root/.local/bin/uv 8 | 9 | # Install `uv` for faster package bootstrapping 10 | ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh 11 | RUN /install.sh && rm /install.sh 12 | 13 | # Copy local context to `/app` inside container (see .dockerignore) 14 | WORKDIR /app 15 | COPY . . 16 | RUN mkdir -p /app/data /app/uploaded_files 17 | 18 | # Create virtualenv which will be copied into final container 19 | ENV VIRTUAL_ENV=/app/.venv 20 | ENV PATH="$VIRTUAL_ENV/bin:$PATH" 21 | RUN $uv venv 22 | 23 | # Install app requirements and reflex inside virtualenv 24 | RUN $uv pip install -r requirements.txt 25 | 26 | # Deploy templates and prepare app 27 | RUN reflex init 28 | 29 | # Export static copy of frontend to /app/.web/_static 30 | RUN reflex export --frontend-only --no-zip 31 | 32 | # Copy static files out of /app to save space in backend image 33 | RUN mv .web/_static /tmp/_static 34 | RUN rm -rf .web && mkdir .web 35 | RUN mv /tmp/_static .web/_static 36 | 37 | # Stage 2: copy artifacts into slim image 38 | FROM python:3.13-slim 39 | WORKDIR /app 40 | RUN adduser --disabled-password --home /app reflex 41 | COPY --chown=reflex --from=init /app /app 42 | # Install libpq-dev for psycopg (skip if not using postgres). 43 | RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* 44 | USER reflex 45 | ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 46 | 47 | # Needed until Reflex properly passes SIGTERM on backend. 48 | STOPSIGNAL SIGKILL 49 | 50 | # Always apply migrations before starting the backend. 51 | CMD [ -d alembic ] && reflex db migrate; \ 52 | exec reflex run --env prod --backend-only 53 | -------------------------------------------------------------------------------- /docker-example/production-compose/compose.prod.yaml: -------------------------------------------------------------------------------- 1 | # Use this override file to run the app in prod mode with postgres and redis 2 | # docker compose -f compose.yaml -f compose.prod.yaml up -d 3 | services: 4 | db: 5 | image: postgres 6 | restart: always 7 | environment: 8 | POSTGRES_PASSWORD: secret 9 | volumes: 10 | - postgres-data:/var/lib/postgresql/data 11 | 12 | redis: 13 | image: redis 14 | restart: always 15 | 16 | app: 17 | environment: 18 | DB_URL: postgresql+psycopg://postgres:secret@db/postgres 19 | REDIS_URL: redis://redis:6379 20 | depends_on: 21 | - db 22 | - redis 23 | 24 | volumes: 25 | postgres-data: 26 | -------------------------------------------------------------------------------- /docker-example/production-compose/compose.tools.yaml: -------------------------------------------------------------------------------- 1 | # Use this override file with `compose.prod.yaml` to run admin tools 2 | # for production services. 3 | # docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d 4 | services: 5 | adminer: 6 | image: adminer 7 | ports: 8 | - 8080:8080 9 | 10 | redis-commander: 11 | image: ghcr.io/joeferner/redis-commander:latest 12 | environment: 13 | - REDIS_HOSTS=local:redis:6379 14 | ports: 15 | - "8081:8081" 16 | 17 | volumes: 18 | redis-ui-settings: 19 | -------------------------------------------------------------------------------- /docker-example/production-compose/compose.yaml: -------------------------------------------------------------------------------- 1 | # Base compose file production deployment of reflex app with Caddy webserver 2 | # providing TLS termination and reverse proxying. 3 | # 4 | # See `compose.prod.yaml` for more robust and performant deployment option. 5 | # 6 | # During build and run, set environment DOMAIN pointing 7 | # to publicly accessible domain where app will be hosted 8 | services: 9 | app: 10 | image: local/reflex-app 11 | environment: 12 | DB_URL: sqlite:///data/reflex.db 13 | build: 14 | context: . 15 | volumes: 16 | - db-data:/app/data 17 | - upload-data:/app/uploaded_files 18 | restart: always 19 | 20 | webserver: 21 | environment: 22 | DOMAIN: ${DOMAIN:-localhost} 23 | ports: 24 | - 443:443 25 | - 80:80 # For acme-challenge via HTTP. 26 | build: 27 | context: . 28 | dockerfile: Caddy.Dockerfile 29 | volumes: 30 | - caddy-data:/root/.caddy 31 | restart: always 32 | depends_on: 33 | - app 34 | 35 | volumes: 36 | # SQLite data 37 | db-data: 38 | # Uploaded files 39 | upload-data: 40 | # TLS keys and certificates 41 | caddy-data: 42 | -------------------------------------------------------------------------------- /docker-example/production-one-port/.dockerignore: -------------------------------------------------------------------------------- 1 | .web 2 | !.web/bun.lockb 3 | !.web/package.json 4 | -------------------------------------------------------------------------------- /docker-example/production-one-port/Caddyfile: -------------------------------------------------------------------------------- 1 | :{$PORT} 2 | 3 | encode gzip 4 | 5 | @backend_routes path /_event/* /ping /_upload /_upload/* 6 | handle @backend_routes { 7 | reverse_proxy localhost:8000 8 | } 9 | 10 | root * /srv 11 | route { 12 | try_files {path} {path}/ /404.html 13 | file_server 14 | } 15 | -------------------------------------------------------------------------------- /docker-example/production-one-port/README.md: -------------------------------------------------------------------------------- 1 | # production-one-port 2 | 3 | This docker deployment runs Reflex in prod mode, exposing a single HTTP port: 4 | 5 | - `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend. 6 | 7 | The deployment also runs a local Redis server to store state for each user. 8 | 9 | Conceptually it is similar to the `simple-one-port` example except it: 10 | 11 | - has layer caching for python, reflex, and node dependencies 12 | - uses multi-stage build to reduce the size of the final image 13 | 14 | Using this method may be preferable for deploying in memory constrained 15 | environments, because it serves a static frontend export, rather than running 16 | the NextJS server via node. 17 | 18 | ## Build 19 | 20 | ```console 21 | docker build -t reflex-production-one-port . 22 | ``` 23 | 24 | ## Run 25 | 26 | ```console 27 | docker run -p 8080:8080 reflex-production-one-port 28 | ``` 29 | 30 | Note that this container has _no persistence_ and will lose all data when 31 | stopped. You can use bind mounts or named volumes to persist the database and 32 | uploaded_files directories as needed. 33 | 34 | ## Usage 35 | 36 | This container should be used with an existing load balancer or reverse proxy to 37 | terminate TLS. 38 | 39 | It is also useful for deploying to simple app platforms, such as Render or Heroku. 40 | -------------------------------------------------------------------------------- /docker-example/simple-one-port/.dockerignore: -------------------------------------------------------------------------------- 1 | .web 2 | .git 3 | __pycache__/* 4 | Dockerfile 5 | uploaded_files 6 | -------------------------------------------------------------------------------- /docker-example/simple-one-port/Caddyfile: -------------------------------------------------------------------------------- 1 | :{$PORT} 2 | 3 | encode gzip 4 | 5 | @backend_routes path /_event/* /ping /_upload /_upload/* 6 | handle @backend_routes { 7 | reverse_proxy localhost:8000 8 | } 9 | 10 | root * /srv 11 | route { 12 | try_files {path} {path}/ /404.html 13 | file_server 14 | } 15 | -------------------------------------------------------------------------------- /docker-example/simple-one-port/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is used to deploy a single-container Reflex app instance 2 | # to services like Render, Railway, Heroku, GCP, and others. 3 | 4 | # It uses a reverse proxy to serve the frontend statically and proxy to backend 5 | # from a single exposed port, expecting TLS termination to be handled at the 6 | # edge by the given platform. 7 | FROM python:3.13 8 | 9 | # If the service expects a different port, provide it here (f.e Render expects port 10000) 10 | ARG PORT=8080 11 | # Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend. 12 | ARG API_URL 13 | ENV PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 14 | 15 | # Install Caddy and redis server inside image 16 | RUN apt-get update -y && apt-get install -y caddy redis-server && rm -rf /var/lib/apt/lists/* 17 | 18 | WORKDIR /app 19 | 20 | # Copy local context to `/app` inside container (see .dockerignore) 21 | COPY . . 22 | 23 | # Install app requirements and reflex in the container 24 | RUN pip install -r requirements.txt 25 | 26 | # Deploy templates and prepare app 27 | RUN reflex init 28 | 29 | # Download all npm dependencies and compile frontend 30 | RUN reflex export --frontend-only --no-zip && mv .web/_static/* /srv/ && rm -rf .web 31 | 32 | # Needed until Reflex properly passes SIGTERM on backend. 33 | STOPSIGNAL SIGKILL 34 | 35 | EXPOSE $PORT 36 | 37 | # Apply migrations before starting the backend. 38 | CMD [ -d alembic ] && reflex db migrate; \ 39 | caddy start && \ 40 | redis-server --daemonize yes && \ 41 | exec reflex run --env prod --backend-only 42 | -------------------------------------------------------------------------------- /docker-example/simple-one-port/README.md: -------------------------------------------------------------------------------- 1 | # simple-one-port 2 | 3 | This docker deployment runs Reflex in prod mode, exposing a single HTTP port: 4 | 5 | - `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend. 6 | 7 | The deployment also runs a local Redis server to store state for each user. 8 | 9 | Using this method may be preferable for deploying in memory constrained 10 | environments, because it serves a static frontend export, rather than running 11 | the NextJS server via node. 12 | 13 | For platforms which only terminate TLS to a single port, this container can be 14 | deployed instead of the `simple-two-port` example. 15 | 16 | ## Build 17 | 18 | ```console 19 | docker build -t reflex-simple-one-port . 20 | ``` 21 | 22 | ## Run 23 | 24 | ```console 25 | docker run -p 8080:8080 reflex-simple-one-port 26 | ``` 27 | 28 | Note that this container has _no persistence_ and will lose all data when 29 | stopped. You can use bind mounts or named volumes to persist the database and 30 | uploaded_files directories as needed. 31 | 32 | ## Usage 33 | 34 | This container should be used with an existing load balancer or reverse proxy to 35 | terminate TLS. 36 | 37 | It is also useful for deploying to simple app platforms, such as Render or Heroku. 38 | -------------------------------------------------------------------------------- /docker-example/simple-two-port/.dockerignore: -------------------------------------------------------------------------------- 1 | .web 2 | .git 3 | __pycache__/* 4 | Dockerfile 5 | uploaded_files 6 | -------------------------------------------------------------------------------- /docker-example/simple-two-port/Dockerfile: -------------------------------------------------------------------------------- 1 | # This Dockerfile is used to deploy a simple single-container Reflex app instance. 2 | FROM python:3.13 3 | 4 | RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/* 5 | ENV REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 6 | 7 | # Copy local context to `/app` inside container (see .dockerignore) 8 | WORKDIR /app 9 | COPY . . 10 | 11 | # Install app requirements and reflex in the container 12 | RUN pip install -r requirements.txt 13 | 14 | # Deploy templates and prepare app 15 | RUN reflex init 16 | 17 | # Download all npm dependencies and compile frontend 18 | RUN reflex export --frontend-only --no-zip 19 | 20 | # Needed until Reflex properly passes SIGTERM on backend. 21 | STOPSIGNAL SIGKILL 22 | 23 | # Always apply migrations before starting the backend. 24 | CMD [ -d alembic ] && reflex db migrate; \ 25 | redis-server --daemonize yes && \ 26 | exec reflex run --env prod 27 | -------------------------------------------------------------------------------- /docker-example/simple-two-port/README.md: -------------------------------------------------------------------------------- 1 | # simple-two-port 2 | 3 | This docker deployment runs Reflex in prod mode, exposing two HTTP ports: 4 | 5 | - `3000` - node NextJS server using optimized production build 6 | - `8000` - python gunicorn server hosting the Reflex backend 7 | 8 | The deployment also runs a local Redis server to store state for each user. 9 | 10 | ## Build 11 | 12 | ```console 13 | docker build -t reflex-simple-two-port . 14 | ``` 15 | 16 | ## Run 17 | 18 | ```console 19 | docker run -p 3000:3000 -p 8000:8000 reflex-simple-two-port 20 | ``` 21 | 22 | Note that this container has _no persistence_ and will lose all data when 23 | stopped. You can use bind mounts or named volumes to persist the database and 24 | uploaded_files directories as needed. 25 | 26 | ## Usage 27 | 28 | This container should be used with an existing load balancer or reverse proxy to 29 | route traffic to the appropriate port inside the container. 30 | 31 | For example, the following Caddyfile can be used to terminate TLS and forward 32 | traffic to the frontend and backend from outside the container. 33 | 34 | ``` 35 | my-domain.com 36 | 37 | encode gzip 38 | 39 | @backend_routes path /_event/* /ping /_upload /_upload/* 40 | handle @backend_routes { 41 | reverse_proxy localhost:8000 42 | } 43 | 44 | reverse_proxy localhost:3000 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/DEBUGGING.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | It is possible to run Reflex apps in dev mode under a debugger. 4 | 5 | 1. Run Reflex as a module: `python -m reflex run --env dev` 6 | 2. Set current working directory to the dir containing `rxconfig.py` 7 | 8 | ## VSCode 9 | 10 | The following launch configuration can be used to interactively debug a Reflex 11 | app with breakpoints. 12 | 13 | ```json 14 | { 15 | "version": "0.2.0", 16 | "configurations": [ 17 | { 18 | "name": "Reflex App", 19 | "type": "python", 20 | "request": "launch", 21 | "module": "reflex", 22 | "args": "run --env dev", 23 | "justMyCode": true, 24 | "cwd": "${fileDirname}/.." 25 | } 26 | ] 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/images/dalle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/docs/images/dalle.gif -------------------------------------------------------------------------------- /docs/images/dalle_colored_code_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/docs/images/dalle_colored_code_example.png -------------------------------------------------------------------------------- /docs/images/reflex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/docs/images/reflex.png -------------------------------------------------------------------------------- /docs/images/reflex.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/reflex_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/images/reflex_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /reflex/.templates/apps/blank/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/reflex/.templates/apps/blank/assets/favicon.ico -------------------------------------------------------------------------------- /reflex/.templates/apps/blank/code/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/reflex/.templates/apps/blank/code/__init__.py -------------------------------------------------------------------------------- /reflex/.templates/apps/blank/code/blank.py: -------------------------------------------------------------------------------- 1 | """Welcome to Reflex! This file outlines the steps to create a basic app.""" 2 | 3 | import reflex as rx 4 | 5 | from rxconfig import config 6 | 7 | 8 | class State(rx.State): 9 | """The app state.""" 10 | 11 | 12 | def index() -> rx.Component: 13 | # Welcome Page (Index) 14 | return rx.container( 15 | rx.color_mode.button(position="top-right"), 16 | rx.vstack( 17 | rx.heading("Welcome to Reflex!", size="9"), 18 | rx.text( 19 | "Get started by editing ", 20 | rx.code(f"{config.app_name}/{config.app_name}.py"), 21 | size="5", 22 | ), 23 | rx.link( 24 | rx.button("Check out our docs!"), 25 | href="https://reflex.dev/docs/getting-started/introduction/", 26 | is_external=True, 27 | ), 28 | spacing="5", 29 | justify="center", 30 | min_height="85vh", 31 | ), 32 | rx.logo(), 33 | ) 34 | 35 | 36 | app = rx.App() 37 | app.add_page(index) 38 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/app/rxconfig.py.jinja2: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | config = rx.Config( 4 | app_name="{{ app_name }}", 5 | plugins=[rx.plugins.TailwindV3Plugin()], 6 | ) 7 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/custom_components/README.md.jinja2: -------------------------------------------------------------------------------- 1 | # {{ module_name }} 2 | 3 | A Reflex custom component {{ module_name }}. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | pip install {{ package_name }} 9 | ``` 10 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/custom_components/__init__.py.jinja2: -------------------------------------------------------------------------------- 1 | from .{{ module_name }} import * -------------------------------------------------------------------------------- /reflex/.templates/jinja/custom_components/demo_app.py.jinja2: -------------------------------------------------------------------------------- 1 | """Welcome to Reflex! This file showcases the custom component in a basic app.""" 2 | 3 | from rxconfig import config 4 | 5 | import reflex as rx 6 | 7 | from {{ custom_component_module_dir }} import {{ module_name }} 8 | 9 | filename = f"{config.app_name}/{config.app_name}.py" 10 | 11 | 12 | class State(rx.State): 13 | """The app state.""" 14 | 15 | pass 16 | 17 | 18 | def index() -> rx.Component: 19 | return rx.center( 20 | rx.theme_panel(), 21 | rx.vstack( 22 | rx.heading("Welcome to Reflex!", size="9"), 23 | rx.text( 24 | "Test your custom component by editing ", 25 | rx.code(filename), 26 | font_size="2em", 27 | ), 28 | {{ module_name }}(), 29 | align="center", 30 | spacing="7", 31 | ), 32 | height="100vh", 33 | ) 34 | 35 | 36 | # Add state and page to the app. 37 | app = rx.App() 38 | app.add_page(index) 39 | 40 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/custom_components/pyproject.toml.jinja2: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "{{ package_name }}" 7 | version = "0.0.1" 8 | description = "Reflex custom component {{ module_name }}" 9 | readme = "README.md" 10 | license = { text = "Apache-2.0" } 11 | requires-python = ">=3.10" 12 | authors = [{ name = "", email = "YOUREMAIL@domain.com" }] 13 | keywords = ["reflex","reflex-custom-components"] 14 | 15 | dependencies = ["reflex>={{ reflex_version }}"] 16 | 17 | classifiers = ["Development Status :: 4 - Beta"] 18 | 19 | [project.urls] 20 | 21 | [project.optional-dependencies] 22 | dev = ["build", "twine"] 23 | 24 | [tool.setuptools.packages.find] 25 | where = ["custom_components"] 26 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/package.json.jinja2: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reflex", 3 | "scripts": { 4 | "dev": "{{ scripts.dev }}", 5 | "export": "{{ scripts.export }}", 6 | "export-sitemap": "{{ scripts.export_sitemap }}", 7 | "prod": "{{ scripts.prod }}" 8 | }, 9 | "dependencies": { 10 | {% for package, version in dependencies.items() %} 11 | "{{ package }}": "{{ version }}"{% if not loop.last %},{% endif %} 12 | 13 | {% endfor %} 14 | }, 15 | "devDependencies": { 16 | {% for package, version in dev_dependencies.items() %} 17 | "{{ package }}": "{{ version }}"{% if not loop.last %},{% endif %} 18 | 19 | {% endfor %} 20 | }, 21 | "overrides": { 22 | {% for package, version in overrides.items() %} 23 | "{{ package }}": "{{ version }}"{% if not loop.last %},{% endif %} 24 | 25 | {% endfor %} 26 | } 27 | } -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/_app.js.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "web/pages/base_page.js.jinja2" %} 2 | {% from "web/pages/macros.js.jinja2" import renderHooks %} 3 | 4 | {% block early_imports %} 5 | import '$/styles/__reflex_global_styles.css' 6 | {% endblock %} 7 | 8 | {% block declaration %} 9 | import { EventLoopProvider, StateProvider, defaultColorMode } from "$/utils/context.js"; 10 | import { ThemeProvider } from 'next-themes' 11 | {% for library_alias, library_path in window_libraries %} 12 | import * as {{library_alias}} from "{{library_path}}"; 13 | {% endfor %} 14 | 15 | {% for custom_code in custom_codes %} 16 | {{custom_code}} 17 | {% endfor %} 18 | {% endblock %} 19 | 20 | {% block export %} 21 | function AppWrap({children}) { 22 | {{ renderHooks(hooks) }} 23 | 24 | return ( 25 | {{utils.render(render)}} 26 | ) 27 | } 28 | 29 | export default function MyApp({ Component, pageProps }) { 30 | React.useEffect(() => { 31 | // Make contexts and state objects available globally for dynamic eval'd components 32 | let windowImports = { 33 | {% for library_alias, library_path in window_libraries %} 34 | "{{library_path}}": {{library_alias}}, 35 | {% endfor %} 36 | }; 37 | window["__reflex"] = windowImports; 38 | }, []); 39 | return ( 40 | jsx(ThemeProvider, {defaultTheme:defaultColorMode,attribute:"class"}, 41 | jsx(StateProvider, {}, 42 | jsx(EventLoopProvider, {}, 43 | jsx(AppWrap, {}, 44 | jsx(Component, pageProps) 45 | ) 46 | ) 47 | ) 48 | ) 49 | ); 50 | } 51 | 52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/_document.js.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "web/pages/base_page.js.jinja2" %} 2 | 3 | {% block export %} 4 | export default function Document() { 5 | return ( 6 | {{utils.render(document)}} 7 | ) 8 | } 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/base_page.js.jinja2: -------------------------------------------------------------------------------- 1 | {% import 'web/pages/utils.js.jinja2' as utils %} 2 | /** @jsxImportSource @emotion/react */ 3 | 4 | {% block early_imports %} 5 | {% endblock %} 6 | 7 | {%- block imports_libs %} 8 | 9 | {% for module in imports%} 10 | {{- utils.get_import(module) }} 11 | {% endfor %} 12 | 13 | {% for dynamic_import in dynamic_imports %} 14 | {{dynamic_import}} 15 | {% endfor %} 16 | {% endblock %} 17 | 18 | {% block declaration %} 19 | {% endblock %} 20 | 21 | {% block export %} 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/component.js.jinja2: -------------------------------------------------------------------------------- 1 | {% import 'web/pages/utils.js.jinja2' as utils %} 2 | {{utils.render(component.render())}} -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/custom_component.js.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "web/pages/base_page.js.jinja2" %} 2 | {% from "web/pages/macros.js.jinja2" import renderHooks %} 3 | 4 | {% block declaration %} 5 | {% for custom_code in custom_codes %} 6 | {{custom_code}} 7 | {% endfor %} 8 | {% endblock %} 9 | 10 | {% block export %} 11 | {% for component in components %} 12 | 13 | export const {{component.name}} = memo(({ {{-component.props|join(", ")-}} }) => { 14 | {{ renderHooks(component.hooks) }} 15 | 16 | return( 17 | {{utils.render(component.render)}} 18 | ) 19 | 20 | }) 21 | {% endfor %} 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/index.js.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "web/pages/base_page.js.jinja2" %} 2 | {% from "web/pages/macros.js.jinja2" import renderHooks %} 3 | 4 | {% block declaration %} 5 | {% for custom_code in custom_codes %} 6 | {{custom_code}} 7 | {% endfor %} 8 | {% endblock %} 9 | 10 | {% block export %} 11 | export default function Component() { 12 | {{ renderHooks(hooks)}} 13 | 14 | return ( 15 | {{utils.render(render)}} 16 | ) 17 | } 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/macros.js.jinja2: -------------------------------------------------------------------------------- 1 | {% macro renderHooks(hooks) %} 2 | {% set sorted_hooks = sort_hooks(hooks) %} 3 | 4 | {# Render the grouped hooks #} 5 | {% for hook, _ in sorted_hooks[const.hook_position.INTERNAL] %} 6 | {{ hook }} 7 | {% endfor %} 8 | 9 | {% for hook, _ in sorted_hooks[const.hook_position.PRE_TRIGGER] %} 10 | {{ hook }} 11 | {% endfor %} 12 | 13 | {% for hook, _ in sorted_hooks[const.hook_position.POST_TRIGGER] %} 14 | {{ hook }} 15 | {% endfor %} 16 | {% endmacro %} 17 | 18 | {% macro renderHooksWithMemo(hooks, memo)%} 19 | {% set sorted_hooks = sort_hooks(hooks) %} 20 | 21 | {# Render the grouped hooks #} 22 | {% for hook, _ in sorted_hooks[const.hook_position.INTERNAL] %} 23 | {{ hook }} 24 | {% endfor %} 25 | 26 | {% for hook, _ in sorted_hooks[const.hook_position.PRE_TRIGGER] %} 27 | {{ hook }} 28 | {% endfor %} 29 | 30 | {% for hook in memo %} 31 | {{ hook }} 32 | {% endfor %} 33 | 34 | {% for hook, _ in sorted_hooks[const.hook_position.POST_TRIGGER] %} 35 | {{ hook }} 36 | {% endfor %} 37 | 38 | {% endmacro %} -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/stateful_component.js.jinja2: -------------------------------------------------------------------------------- 1 | {% import 'web/pages/utils.js.jinja2' as utils %} 2 | {% from 'web/pages/macros.js.jinja2' import renderHooksWithMemo %} 3 | {% set all_hooks = component._get_all_hooks() %} 4 | 5 | export function {{tag_name}} () { 6 | {{ renderHooksWithMemo(all_hooks, memo_trigger_hooks) }} 7 | 8 | return ( 9 | {{utils.render(component.render())}} 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/pages/stateful_components.js.jinja2: -------------------------------------------------------------------------------- 1 | {% extends "web/pages/base_page.js.jinja2" %} 2 | 3 | {% block export %} 4 | {{ memoized_code }} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/styles/styles.css.jinja2: -------------------------------------------------------------------------------- 1 | {%- block imports_styles %} 2 | {% for sheet_name in stylesheets %} 3 | {{- "@import url('" + sheet_name + "'); " }} 4 | {% endfor %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /reflex/.templates/jinja/web/utils/theme.js.jinja2: -------------------------------------------------------------------------------- 1 | export default {{ theme }} -------------------------------------------------------------------------------- /reflex/.templates/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /_static 4 | 5 | # dependencies 6 | /node_modules 7 | /.pnp 8 | .pnp.js 9 | 10 | # testing 11 | /coverage 12 | 13 | # next.js 14 | /.next/ 15 | /out/ 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env.local 31 | .env.development.local 32 | .env.test.local 33 | .env.production.local 34 | 35 | # vercel 36 | .vercel 37 | 38 | # DS_Store 39 | .DS_Store -------------------------------------------------------------------------------- /reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js: -------------------------------------------------------------------------------- 1 | import { useTheme } from "next-themes"; 2 | import { useRef, useEffect, useState, createElement } from "react"; 3 | import { 4 | ColorModeContext, 5 | defaultColorMode, 6 | isDevMode, 7 | lastCompiledTimeStamp, 8 | } from "$/utils/context.js"; 9 | 10 | export default function RadixThemesColorModeProvider({ children }) { 11 | const { theme, resolvedTheme, setTheme } = useTheme(); 12 | const [rawColorMode, setRawColorMode] = useState(defaultColorMode); 13 | const [resolvedColorMode, setResolvedColorMode] = useState( 14 | defaultColorMode === "dark" ? "dark" : "light", 15 | ); 16 | const firstUpdate = useRef(true); 17 | useEffect(() => { 18 | if (firstUpdate.current) { 19 | firstUpdate.current = false; 20 | setRawColorMode(theme); 21 | setResolvedColorMode(resolvedTheme); 22 | } 23 | }); 24 | 25 | useEffect(() => { 26 | if (isDevMode) { 27 | const lastCompiledTimeInLocalStorage = 28 | localStorage.getItem("last_compiled_time"); 29 | if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) { 30 | // on app startup, make sure the application color mode is persisted correctly. 31 | setTheme(defaultColorMode); 32 | localStorage.setItem("last_compiled_time", lastCompiledTimeStamp); 33 | return; 34 | } 35 | } 36 | setRawColorMode(theme); 37 | setResolvedColorMode(resolvedTheme); 38 | }, [theme, resolvedTheme]); 39 | 40 | const toggleColorMode = () => { 41 | setTheme(resolvedTheme === "light" ? "dark" : "light"); 42 | }; 43 | const setColorMode = (mode) => { 44 | const allowedModes = ["light", "dark", "system"]; 45 | if (!allowedModes.includes(mode)) { 46 | console.error( 47 | `Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`, 48 | ); 49 | mode = defaultColorMode; 50 | } 51 | setTheme(mode); 52 | }; 53 | return createElement( 54 | ColorModeContext, 55 | { 56 | value: { rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }, 57 | }, 58 | children, 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /reflex/.templates/web/components/shiki/code.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, createElement } from "react"; 2 | import { codeToHtml } from "shiki"; 3 | 4 | /** 5 | * Code component that uses Shiki to convert code to HTML and render it. 6 | * 7 | * @param code - The code to be highlighted. 8 | * @param theme - The theme to be used for highlighting. 9 | * @param language - The language of the code. 10 | * @param transformers - The transformers to be applied to the code. 11 | * @param decorations - The decorations to be applied to the code. 12 | * @param divProps - Additional properties to be passed to the div element. 13 | * @returns The rendered code block. 14 | */ 15 | export function Code({ 16 | code, 17 | theme, 18 | language, 19 | transformers, 20 | decorations, 21 | ...divProps 22 | }) { 23 | const [codeResult, setCodeResult] = useState(""); 24 | useEffect(() => { 25 | async function fetchCode() { 26 | const result = await codeToHtml(code, { 27 | lang: language, 28 | theme, 29 | transformers, 30 | decorations, 31 | }); 32 | setCodeResult(result); 33 | } 34 | fetchCode(); 35 | }, [code, language, theme, transformers, decorations]); 36 | return createElement("div", { 37 | dangerouslySetInnerHTML: { __html: codeResult }, 38 | ...divProps, 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /reflex/.templates/web/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "$/*": ["*"], 6 | "@/*": ["public/*"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /reflex/.templates/web/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | basePath: "", 3 | compress: true, 4 | reactStrictMode: true, 5 | trailingSlash: true, 6 | output: "", 7 | }; 8 | -------------------------------------------------------------------------------- /reflex/.templates/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "postcss-import": {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/client_side_routing.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import { useRouter } from "next/router"; 3 | 4 | /** 5 | * React hook for use in /404 page to enable client-side routing. 6 | * 7 | * Uses the next/router to redirect to the provided URL when loading 8 | * the 404 page (for example as a fallback in static hosting situations). 9 | * 10 | * @returns {boolean} routeNotFound - true if the current route is an actual 404 11 | */ 12 | export const useClientSideRouting = () => { 13 | const [routeNotFound, setRouteNotFound] = useState(false); 14 | const didRedirect = useRef(false); 15 | const router = useRouter(); 16 | useEffect(() => { 17 | if ( 18 | router.isReady && 19 | !didRedirect.current // have not tried redirecting yet 20 | ) { 21 | didRedirect.current = true; // never redirect twice to avoid "Hard Navigate" error 22 | // attempt to redirect to the route in the browser address bar once 23 | router 24 | .replace({ 25 | pathname: window.location.pathname, 26 | query: window.location.search.slice(1), 27 | }) 28 | .then(() => { 29 | // Check if the current route is /404 30 | if (router.pathname === "/404") { 31 | setRouteNotFound(true); // Mark as an actual 404 32 | } 33 | }) 34 | .catch((e) => { 35 | setRouteNotFound(true); // navigation failed, so this is a real 404 36 | }); 37 | } 38 | }, [router.isReady]); 39 | 40 | // Return the reactive bool, to avoid flashing 404 page until we know for sure 41 | // the route is not found. 42 | return routeNotFound; 43 | }; 44 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/helpers/dataeditor.js: -------------------------------------------------------------------------------- 1 | import { GridCellKind } from "@glideapps/glide-data-grid"; 2 | 3 | export function getDEColumn(columns, col) { 4 | let c = columns[col]; 5 | c.pos = col; 6 | return c; 7 | } 8 | 9 | export function getDERow(data, row) { 10 | return data[row]; 11 | } 12 | 13 | export function locateCell(row, column) { 14 | if (Array.isArray(row)) { 15 | return row[column.pos]; 16 | } else { 17 | return row[column.id]; 18 | } 19 | } 20 | 21 | export function formatCell(value, column) { 22 | const editable = column.editable ?? true; 23 | switch (column.type) { 24 | case "int": 25 | case "float": 26 | return { 27 | kind: GridCellKind.Number, 28 | data: value, 29 | displayData: value + "", 30 | readonly: !editable, 31 | allowOverlay: editable, 32 | }; 33 | case "datetime": 34 | // value = moment format? 35 | case "str": 36 | return { 37 | kind: GridCellKind.Text, 38 | data: value, 39 | displayData: value, 40 | readonly: !editable, 41 | allowOverlay: editable, 42 | }; 43 | case "bool": 44 | return { 45 | kind: GridCellKind.Boolean, 46 | data: value, 47 | readonly: !editable, 48 | }; 49 | default: 50 | console.log( 51 | "Warning: column.type is undefined for column.title=" + column.title, 52 | ); 53 | return { 54 | kind: GridCellKind.Text, 55 | data: value, 56 | displayData: column.type, 57 | }; 58 | } 59 | } 60 | 61 | export function formatDataEditorCells(col, row, columns, data) { 62 | if (row < data.length && col < columns.length) { 63 | const column = getDEColumn(columns, col); 64 | const rowData = getDERow(data, row); 65 | const cellData = locateCell(rowData, column); 66 | return formatCell(cellData, column); 67 | } 68 | return { kind: GridCellKind.Loading }; 69 | } 70 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/helpers/debounce.js: -------------------------------------------------------------------------------- 1 | const debounce_timeout_id = {}; 2 | 3 | /** 4 | * Generic debounce helper 5 | * 6 | * @param {string} name - the name of the event to debounce 7 | * @param {function} func - the function to call after debouncing 8 | * @param {number} delay - the time in milliseconds to wait before calling the function 9 | */ 10 | export default function debounce(name, func, delay) { 11 | const key = `${name}__${delay}`; 12 | clearTimeout(debounce_timeout_id[key]); 13 | debounce_timeout_id[key] = setTimeout(() => { 14 | func(); 15 | delete debounce_timeout_id[key]; 16 | }, delay); 17 | } 18 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/helpers/paste.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | const handle_paste_data = (clipboardData) => 4 | new Promise((resolve, reject) => { 5 | const pasted_data = []; 6 | const n_items = clipboardData.items.length; 7 | const extract_data = (item) => { 8 | const type = item.type; 9 | if (item.kind === "string") { 10 | item.getAsString((data) => { 11 | pasted_data.push([type, data]); 12 | if (pasted_data.length === n_items) { 13 | resolve(pasted_data); 14 | } 15 | }); 16 | } else if (item.kind === "file") { 17 | const file = item.getAsFile(); 18 | const reader = new FileReader(); 19 | reader.onload = (e) => { 20 | pasted_data.push([type, e.target.result]); 21 | if (pasted_data.length === n_items) { 22 | resolve(pasted_data); 23 | } 24 | }; 25 | if (type.indexOf("text/") === 0) { 26 | reader.readAsText(file); 27 | } else { 28 | reader.readAsDataURL(file); 29 | } 30 | } 31 | }; 32 | for (const item of clipboardData.items) { 33 | extract_data(item); 34 | } 35 | }); 36 | 37 | export default function usePasteHandler(target_ids, event_actions, on_paste) { 38 | return useEffect(() => { 39 | const handle_paste = (_ev) => { 40 | event_actions.preventDefault && _ev.preventDefault(); 41 | event_actions.stopPropagation && _ev.stopPropagation(); 42 | handle_paste_data(_ev.clipboardData).then(on_paste); 43 | }; 44 | const targets = target_ids 45 | .map((id) => document.getElementById(id)) 46 | .filter((element) => !!element); 47 | if (target_ids.length === 0) { 48 | targets.push(document); 49 | } 50 | targets.forEach((target) => 51 | target.addEventListener("paste", handle_paste, false), 52 | ); 53 | return () => { 54 | targets.forEach((target) => 55 | target.removeEventListener("paste", handle_paste, false), 56 | ); 57 | }; 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/helpers/range.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulate the python range() builtin function. 3 | * inspired by https://dev.to/guyariely/using-python-range-in-javascript-337p 4 | * 5 | * If needed outside of an iterator context, use `Array.from(range(10))` or 6 | * spread syntax `[...range(10)]` to get an array. 7 | * 8 | * @param {number} start: the start or end of the range. 9 | * @param {number} stop: the end of the range. 10 | * @param {number} step: the step of the range. 11 | * @returns {object} an object with a Symbol.iterator method over the range 12 | */ 13 | export default function range(start, stop, step) { 14 | return { 15 | [Symbol.iterator]() { 16 | if (stop === undefined) { 17 | stop = start; 18 | start = 0; 19 | } 20 | if (step === undefined) { 21 | step = 1; 22 | } 23 | 24 | let i = start - step; 25 | 26 | return { 27 | next() { 28 | i += step; 29 | if ((step > 0 && i < stop) || (step < 0 && i > stop)) { 30 | return { 31 | value: i, 32 | done: false, 33 | }; 34 | } 35 | return { 36 | value: undefined, 37 | done: true, 38 | }; 39 | }, 40 | }; 41 | }, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /reflex/.templates/web/utils/helpers/throttle.js: -------------------------------------------------------------------------------- 1 | const in_throttle = {}; 2 | 3 | /** 4 | * Generic throttle helper 5 | * 6 | * @param {string} name - the name of the event to throttle 7 | * @param {number} limit - time in milliseconds between events 8 | * @returns true if the event is allowed to execute, false if it is throttled 9 | */ 10 | export default function throttle(name, limit) { 11 | const key = `${name}__${limit}`; 12 | if (!in_throttle[key]) { 13 | in_throttle[key] = true; 14 | 15 | setTimeout(() => { 16 | delete in_throttle[key]; 17 | }, limit); 18 | // function was not throttled, so allow execution 19 | return true; 20 | } 21 | return false; 22 | } 23 | -------------------------------------------------------------------------------- /reflex/__main__.py: -------------------------------------------------------------------------------- 1 | """reflex package invocation entry point.""" 2 | 3 | from .reflex import cli 4 | 5 | if __name__ == "__main__": 6 | cli() 7 | -------------------------------------------------------------------------------- /reflex/admin.py: -------------------------------------------------------------------------------- 1 | """The Reflex Admin Dashboard.""" 2 | 3 | from __future__ import annotations 4 | 5 | from dataclasses import dataclass, field 6 | from typing import TYPE_CHECKING 7 | 8 | if TYPE_CHECKING: 9 | from starlette_admin.base import BaseAdmin as Admin 10 | 11 | 12 | @dataclass 13 | class AdminDash: 14 | """Data used to build the admin dashboard.""" 15 | 16 | models: list = field(default_factory=list) 17 | view_overrides: dict = field(default_factory=dict) 18 | admin: Admin | None = None 19 | -------------------------------------------------------------------------------- /reflex/app_mixins/__init__.py: -------------------------------------------------------------------------------- 1 | """Reflex AppMixins package.""" 2 | 3 | from .lifespan import LifespanMixin 4 | from .middleware import MiddlewareMixin 5 | from .mixin import AppMixin 6 | -------------------------------------------------------------------------------- /reflex/app_mixins/mixin.py: -------------------------------------------------------------------------------- 1 | """Default mixin for all app mixins.""" 2 | 3 | import dataclasses 4 | 5 | 6 | @dataclasses.dataclass 7 | class AppMixin: 8 | """Define the base class for all app mixins.""" 9 | 10 | def _init_mixin(self): 11 | """Initialize the mixin. 12 | 13 | Any App mixin can override this method to perform any initialization. 14 | """ 15 | -------------------------------------------------------------------------------- /reflex/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | """The Reflex compiler.""" 2 | -------------------------------------------------------------------------------- /reflex/components/__init__.py: -------------------------------------------------------------------------------- 1 | """Import all the components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | _SUBMODULES: set[str] = { 8 | "lucide", 9 | "core", 10 | "datadisplay", 11 | "gridjs", 12 | "markdown", 13 | "moment", 14 | "plotly", 15 | "radix", 16 | "react_player", 17 | "sonner", 18 | "suneditor", 19 | "el", 20 | "base", 21 | "recharts", 22 | } 23 | 24 | _SUBMOD_ATTRS: dict[str, list[str]] = { 25 | "component": [ 26 | "Component", 27 | "NoSSRComponent", 28 | ], 29 | "next": ["NextLink", "next_link"], 30 | } 31 | __getattr__, __dir__, __all__ = lazy_loader.attach( 32 | __name__, 33 | submodules=_SUBMODULES, 34 | submod_attrs=_SUBMOD_ATTRS, 35 | ) 36 | -------------------------------------------------------------------------------- /reflex/components/base/__init__.py: -------------------------------------------------------------------------------- 1 | """Base components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | _SUBMODULES: set[str] = {"app_wrap", "bare"} 8 | 9 | _SUBMOD_ATTRS: dict[str, list[str]] = { 10 | "body": ["Body"], 11 | "document": ["DocumentHead", "Html", "Main", "NextScript"], 12 | "fragment": [ 13 | "Fragment", 14 | "fragment", 15 | ], 16 | "error_boundary": [ 17 | "ErrorBoundary", 18 | "error_boundary", 19 | ], 20 | "head": [ 21 | "head", 22 | "Head", 23 | ], 24 | "link": ["RawLink", "ScriptTag"], 25 | "meta": ["Description", "Image", "Meta", "Title"], 26 | "script": ["Script", "script"], 27 | } 28 | 29 | __getattr__, __dir__, __all__ = lazy_loader.attach( 30 | __name__, 31 | submodules=_SUBMODULES, 32 | submod_attrs=_SUBMOD_ATTRS, 33 | ) 34 | -------------------------------------------------------------------------------- /reflex/components/base/app_wrap.py: -------------------------------------------------------------------------------- 1 | """Top-level component that wraps the entire app.""" 2 | 3 | from reflex.components.base.fragment import Fragment 4 | from reflex.components.component import Component 5 | from reflex.vars.base import Var 6 | 7 | 8 | class AppWrap(Fragment): 9 | """Top-level component that wraps the entire app.""" 10 | 11 | @classmethod 12 | def create(cls) -> Component: 13 | """Create a new AppWrap component. 14 | 15 | Returns: 16 | A new AppWrap component containing {children}. 17 | """ 18 | return super().create(Var(_js_expr="children")) 19 | -------------------------------------------------------------------------------- /reflex/components/base/body.py: -------------------------------------------------------------------------------- 1 | """Display the page body.""" 2 | 3 | from reflex.components.el import elements 4 | 5 | 6 | class Body(elements.Body): 7 | """A body component.""" 8 | -------------------------------------------------------------------------------- /reflex/components/base/document.py: -------------------------------------------------------------------------------- 1 | """Document components.""" 2 | 3 | from reflex.components.component import Component 4 | 5 | 6 | class NextDocumentLib(Component): 7 | """Root document components.""" 8 | 9 | library = "next/document" 10 | 11 | 12 | class Html(NextDocumentLib): 13 | """The document html.""" 14 | 15 | tag = "Html" 16 | 17 | lang: str | None 18 | 19 | 20 | class DocumentHead(NextDocumentLib): 21 | """The document head.""" 22 | 23 | tag = "Head" 24 | 25 | 26 | class Main(NextDocumentLib): 27 | """The document main section.""" 28 | 29 | tag = "Main" 30 | 31 | 32 | class NextScript(NextDocumentLib): 33 | """The document main scripts.""" 34 | 35 | tag = "NextScript" 36 | -------------------------------------------------------------------------------- /reflex/components/base/fragment.py: -------------------------------------------------------------------------------- 1 | """React fragments to enable bare returns of component trees from functions.""" 2 | 3 | from reflex.components.component import Component 4 | 5 | 6 | class Fragment(Component): 7 | """A React fragment to return multiple components from a function without wrapping it in a container.""" 8 | 9 | library = "react" 10 | tag = "Fragment" 11 | 12 | 13 | fragment = Fragment.create 14 | -------------------------------------------------------------------------------- /reflex/components/base/head.py: -------------------------------------------------------------------------------- 1 | """The head component.""" 2 | 3 | from reflex.components.component import Component, MemoizationLeaf 4 | 5 | 6 | class NextHeadLib(Component): 7 | """Header components.""" 8 | 9 | library = "next/head" 10 | 11 | 12 | class Head(NextHeadLib, MemoizationLeaf): 13 | """Head Component.""" 14 | 15 | tag = "NextHead" 16 | 17 | is_default = True 18 | 19 | 20 | head = Head.create 21 | -------------------------------------------------------------------------------- /reflex/components/base/link.py: -------------------------------------------------------------------------------- 1 | """Display the title of the current page.""" 2 | 3 | from reflex.components.el.elements.base import BaseHTML 4 | from reflex.vars.base import Var 5 | 6 | 7 | class RawLink(BaseHTML): 8 | """A component that displays the title of the current page.""" 9 | 10 | tag = "link" 11 | 12 | # The href. 13 | href: Var[str] 14 | 15 | # The type of link. 16 | rel: Var[str] 17 | 18 | 19 | class ScriptTag(BaseHTML): 20 | """A script tag with the specified type and source.""" 21 | 22 | tag = "script" 23 | 24 | # The type of script represented. 25 | type_: Var[str] 26 | 27 | # The URI of an external script. 28 | source: Var[str] 29 | 30 | # Metadata to verify the content of the script. 31 | integrity: Var[str] 32 | 33 | # Whether to allow cross-origin requests. 34 | crossorigin: Var[str] 35 | 36 | # Indicates which referrer to send when fetching the script. 37 | referrer_policy: Var[str] 38 | 39 | # Whether to asynchronously load the script. 40 | is_async: Var[bool] 41 | 42 | # Whether to defer loading the script. 43 | defer: Var[bool] 44 | -------------------------------------------------------------------------------- /reflex/components/base/meta.py: -------------------------------------------------------------------------------- 1 | """Display the title of the current page.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.components.base.bare import Bare 6 | from reflex.components.el import elements 7 | 8 | 9 | class Title(elements.Title): 10 | """A component that displays the title of the current page.""" 11 | 12 | def render(self) -> dict: 13 | """Render the title component. 14 | 15 | Raises: 16 | ValueError: If the title is not a single string. 17 | 18 | Returns: 19 | The rendered title component. 20 | """ 21 | # Make sure the title is a single string. 22 | if len(self.children) != 1 or not isinstance(self.children[0], Bare): 23 | msg = "Title must be a single string." 24 | raise ValueError(msg) 25 | return super().render() 26 | 27 | 28 | class Meta(elements.Meta): 29 | """A component that displays metadata for the current page.""" 30 | 31 | # The description of character encoding. 32 | char_set: str | None = None 33 | 34 | # The value of meta. 35 | content: str | None = None 36 | 37 | # The name of metadata. 38 | name: str | None = None 39 | 40 | # The type of metadata value. 41 | property: str | None = None 42 | 43 | # The type of metadata value. 44 | http_equiv: str | None = None 45 | 46 | 47 | class Description(elements.Meta): 48 | """A component that displays the title of the current page.""" 49 | 50 | # The type of the description. 51 | name: str | None = "description" 52 | 53 | 54 | class Image(elements.Meta): 55 | """A component that displays the title of the current page.""" 56 | 57 | # The type of the image. 58 | property: str | None = "og:image" 59 | -------------------------------------------------------------------------------- /reflex/components/base/strict_mode.py: -------------------------------------------------------------------------------- 1 | """Module for the StrictMode component.""" 2 | 3 | from reflex.components.component import Component 4 | 5 | 6 | class StrictMode(Component): 7 | """A React strict mode component to enable strict mode for its children.""" 8 | 9 | library = "react" 10 | tag = "StrictMode" 11 | -------------------------------------------------------------------------------- /reflex/components/core/__init__.py: -------------------------------------------------------------------------------- 1 | """Core Reflex components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | _SUBMODULES: set[str] = {"layout"} 8 | 9 | _SUBMOD_ATTRS: dict[str, list[str]] = { 10 | "banner": [ 11 | "ConnectionBanner", 12 | "ConnectionModal", 13 | "ConnectionPulser", 14 | "ConnectionToaster", 15 | "connection_banner", 16 | "connection_modal", 17 | "connection_toaster", 18 | "connection_pulser", 19 | ], 20 | "clipboard": ["Clipboard", "clipboard"], 21 | "colors": [ 22 | "color", 23 | ], 24 | "cond": ["Cond", "color_mode_cond", "cond"], 25 | "debounce": ["DebounceInput", "debounce_input"], 26 | "foreach": [ 27 | "foreach", 28 | "Foreach", 29 | ], 30 | "html": ["html", "Html"], 31 | "match": [ 32 | "match", 33 | "Match", 34 | ], 35 | "breakpoints": ["breakpoints", "set_breakpoints"], 36 | "responsive": [ 37 | "desktop_only", 38 | "mobile_and_tablet", 39 | "mobile_only", 40 | "tablet_and_desktop", 41 | "tablet_only", 42 | ], 43 | "upload": [ 44 | "upload", 45 | "cancel_upload", 46 | "clear_selected_files", 47 | "get_upload_dir", 48 | "get_upload_url", 49 | "selected_files", 50 | ], 51 | "auto_scroll": ["auto_scroll"], 52 | } 53 | 54 | __getattr__, __dir__, __all__ = lazy_loader.attach( 55 | __name__, 56 | submodules=_SUBMODULES, 57 | submod_attrs=_SUBMOD_ATTRS, 58 | ) 59 | -------------------------------------------------------------------------------- /reflex/components/core/client_side_routing.py: -------------------------------------------------------------------------------- 1 | """Handle dynamic routes in static exports via client-side routing. 2 | 3 | Works with /utils/client_side_routing.js to handle the redirect and state. 4 | 5 | When the user hits a 404 accessing a route, redirect them to the same page, 6 | setting a reactive state var "routeNotFound" to true if the redirect fails. The 7 | `wait_for_client_redirect` function will render the component only after 8 | routeNotFound becomes true. 9 | """ 10 | 11 | from __future__ import annotations 12 | 13 | from reflex import constants 14 | from reflex.components.component import Component 15 | from reflex.components.core.cond import cond 16 | from reflex.vars.base import Var 17 | 18 | route_not_found: Var = Var(_js_expr=constants.ROUTE_NOT_FOUND) 19 | 20 | 21 | class ClientSideRouting(Component): 22 | """The client-side routing component.""" 23 | 24 | library = "$/utils/client_side_routing" 25 | tag = "useClientSideRouting" 26 | 27 | def add_hooks(self) -> list[str | Var]: 28 | """Get the hooks to render. 29 | 30 | Returns: 31 | The useClientSideRouting hook. 32 | """ 33 | return [f"const {constants.ROUTE_NOT_FOUND} = {self.tag}()"] 34 | 35 | def render(self) -> str: 36 | """Render the component. 37 | 38 | Returns: 39 | Empty string, because this component is only used for its hooks. 40 | """ 41 | return "" 42 | 43 | 44 | def wait_for_client_redirect(component: Component) -> Component: 45 | """Wait for a redirect to occur before rendering a component. 46 | 47 | This prevents the 404 page from flashing while the redirect is happening. 48 | 49 | Args: 50 | component: The component to render after the redirect. 51 | 52 | Returns: 53 | The conditionally rendered component. 54 | """ 55 | return cond( 56 | route_not_found, 57 | component, 58 | ClientSideRouting.create(), 59 | ) 60 | 61 | 62 | class Default404Page(Component): 63 | """The NextJS default 404 page.""" 64 | 65 | library = "next/error" 66 | tag = "Error" 67 | is_default = True 68 | 69 | status_code: Var[int] = Var.create(404) 70 | -------------------------------------------------------------------------------- /reflex/components/core/colors.py: -------------------------------------------------------------------------------- 1 | """The colors used in Reflex are a wrapper around https://www.radix-ui.com/colors.""" 2 | 3 | from reflex.constants.base import REFLEX_VAR_OPENING_TAG 4 | from reflex.constants.colors import ( 5 | COLORS, 6 | MAX_SHADE_VALUE, 7 | MIN_SHADE_VALUE, 8 | Color, 9 | ColorType, 10 | ShadeType, 11 | ) 12 | from reflex.vars.base import Var 13 | 14 | 15 | def color( 16 | color: ColorType | Var[str], 17 | shade: ShadeType | Var[int] = 7, 18 | alpha: bool | Var[bool] = False, 19 | ) -> Color: 20 | """Create a color object. 21 | 22 | Args: 23 | color: The color to use. 24 | shade: The shade of the color to use. 25 | alpha: Whether to use the alpha variant of the color. 26 | 27 | Returns: 28 | The color object. 29 | 30 | Raises: 31 | ValueError: If the color, shade, or alpha are not valid. 32 | """ 33 | if isinstance(color, str): 34 | if color not in COLORS and REFLEX_VAR_OPENING_TAG not in color: 35 | msg = f"Color must be one of {COLORS}, received {color}" 36 | raise ValueError(msg) 37 | elif not isinstance(color, Var): 38 | msg = "Color must be a string or a Var" 39 | raise ValueError(msg) 40 | 41 | if isinstance(shade, int): 42 | if shade < MIN_SHADE_VALUE or shade > MAX_SHADE_VALUE: 43 | msg = f"Shade must be between {MIN_SHADE_VALUE} and {MAX_SHADE_VALUE}" 44 | raise ValueError(msg) 45 | elif not isinstance(shade, Var): 46 | msg = "Shade must be an integer or a Var" 47 | raise ValueError(msg) 48 | 49 | if not isinstance(alpha, (bool, Var)): 50 | msg = "Alpha must be a boolean or a Var" 51 | raise ValueError(msg) 52 | 53 | return Color(color, shade, alpha) 54 | -------------------------------------------------------------------------------- /reflex/components/core/html.py: -------------------------------------------------------------------------------- 1 | """A html component.""" 2 | 3 | from reflex.components.el.elements.typography import Div 4 | from reflex.vars.base import Var 5 | 6 | 7 | class Html(Div): 8 | """Render the html. 9 | 10 | Returns: 11 | The code to render the html component. 12 | """ 13 | 14 | # The HTML to render. 15 | dangerouslySetInnerHTML: Var[dict[str, str]] # noqa: N815 16 | 17 | @classmethod 18 | def create(cls, *children, **props): 19 | """Create a html component. 20 | 21 | Args: 22 | *children: The children of the component. 23 | **props: The props to pass to the component. 24 | 25 | Returns: 26 | The html component. 27 | 28 | Raises: 29 | ValueError: If children are not provided or more than one child is provided. 30 | """ 31 | # If children are not provided, throw an error. 32 | if len(children) != 1: 33 | msg = "Must provide children to the html component." 34 | raise ValueError(msg) 35 | props["dangerouslySetInnerHTML"] = {"__html": children[0]} 36 | 37 | # Apply the default classname 38 | given_class_name = props.pop("class_name", []) 39 | if isinstance(given_class_name, str): 40 | given_class_name = [given_class_name] 41 | props["class_name"] = ["rx-Html", *given_class_name] 42 | 43 | # Create the component. 44 | return super().create(**props) 45 | 46 | 47 | html = Html.create 48 | -------------------------------------------------------------------------------- /reflex/components/core/layout/__init__.py: -------------------------------------------------------------------------------- 1 | """Core layout components.""" 2 | -------------------------------------------------------------------------------- /reflex/components/core/responsive.py: -------------------------------------------------------------------------------- 1 | """Responsive components.""" 2 | 3 | from reflex.components.radix.themes.layout.box import Box 4 | 5 | 6 | # Add responsive styles shortcuts. 7 | def mobile_only(*children, **props): 8 | """Create a component that is only visible on mobile. 9 | 10 | Args: 11 | *children: The children to pass to the component. 12 | **props: The props to pass to the component. 13 | 14 | Returns: 15 | The component. 16 | """ 17 | return Box.create(*children, **props, display=["block", "none", "none", "none"]) 18 | 19 | 20 | def tablet_only(*children, **props): 21 | """Create a component that is only visible on tablet. 22 | 23 | Args: 24 | *children: The children to pass to the component. 25 | **props: The props to pass to the component. 26 | 27 | Returns: 28 | The component. 29 | """ 30 | return Box.create(*children, **props, display=["none", "block", "block", "none"]) 31 | 32 | 33 | def desktop_only(*children, **props): 34 | """Create a component that is only visible on desktop. 35 | 36 | Args: 37 | *children: The children to pass to the component. 38 | **props: The props to pass to the component. 39 | 40 | Returns: 41 | The component. 42 | """ 43 | return Box.create(*children, **props, display=["none", "none", "none", "block"]) 44 | 45 | 46 | def tablet_and_desktop(*children, **props): 47 | """Create a component that is only visible on tablet and desktop. 48 | 49 | Args: 50 | *children: The children to pass to the component. 51 | **props: The props to pass to the component. 52 | 53 | Returns: 54 | The component. 55 | """ 56 | return Box.create(*children, **props, display=["none", "block", "block", "block"]) 57 | 58 | 59 | def mobile_and_tablet(*children, **props): 60 | """Create a component that is only visible on mobile and tablet. 61 | 62 | Args: 63 | *children: The children to pass to the component. 64 | **props: The props to pass to the component. 65 | 66 | Returns: 67 | The component. 68 | """ 69 | return Box.create(*children, **props, display=["block", "block", "block", "none"]) 70 | -------------------------------------------------------------------------------- /reflex/components/datadisplay/__init__.py: -------------------------------------------------------------------------------- 1 | """Data grid components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | _SUBMOD_ATTRS: dict[str, list[str]] = { 8 | "code": [ 9 | "CodeBlock", 10 | "code_block", 11 | "LiteralCodeLanguage", 12 | ], 13 | "dataeditor": ["data_editor", "data_editor_theme", "DataEditorTheme"], 14 | "logo": ["logo"], 15 | } 16 | 17 | __getattr__, __dir__, __all__ = lazy_loader.attach( 18 | __name__, 19 | submod_attrs=_SUBMOD_ATTRS, 20 | ) 21 | -------------------------------------------------------------------------------- /reflex/components/el/__init__.py: -------------------------------------------------------------------------------- 1 | """The el package exports raw HTML elements.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | from . import elements 8 | 9 | _SUBMODULES: set[str] = {"elements"} 10 | _SUBMOD_ATTRS: dict[str, list[str]] = { 11 | f"elements.{k}": v for k, v in elements._MAPPING.items() 12 | } 13 | 14 | __getattr__, __dir__, __all__ = lazy_loader.attach( 15 | __name__, 16 | submodules=_SUBMODULES, 17 | submod_attrs=_SUBMOD_ATTRS, 18 | ) 19 | -------------------------------------------------------------------------------- /reflex/components/el/constants/__init__.py: -------------------------------------------------------------------------------- 1 | """Constants used to compile element classes.""" 2 | 3 | from .html import * 4 | from .react import * 5 | from .reflex import * 6 | -------------------------------------------------------------------------------- /reflex/components/el/constants/reflex.py: -------------------------------------------------------------------------------- 1 | """Constants used to compile element classes.""" 2 | 3 | from collections import defaultdict 4 | 5 | from reflex.utils import format 6 | 7 | from .html import ATTR_TO_ELEMENTS 8 | from .react import POSSIBLE_STANDARD_NAMES 9 | 10 | # Maps HTML attributes that are invalid Python identifiers to their Reflex 11 | # prop equivalents. 12 | ATTR_TO_PROP_OVERRIDES = { 13 | "async": "async_", # `async` is a reserved keyword in Python. 14 | } 15 | 16 | 17 | def attr_to_prop(attr_name: str) -> str: 18 | """Convert an HTML attribute name to its Reflex name. 19 | 20 | This function first uses React's `possibleStandardNames` to convert the 21 | HTML attribute name to its standard React name, then converts the standard 22 | name to a Reflex name. 23 | 24 | Args: 25 | attr_name: The HTML attribute name. 26 | 27 | Returns: 28 | A Reflex prop name that maps to the HTML attribute. 29 | """ 30 | if attr_name in ATTR_TO_PROP_OVERRIDES: 31 | return ATTR_TO_PROP_OVERRIDES[attr_name] 32 | return format.to_snake_case(POSSIBLE_STANDARD_NAMES.get(attr_name, attr_name)) 33 | 34 | 35 | # Names of HTML attributes that are provided by Reflex out of the box. 36 | REFLEX_PROVIDED_ATTRS = {"class", "id", "style"} 37 | 38 | # ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as 39 | # Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex 40 | # prop names. It omits props that are provided by Reflex out of the box. 41 | PROP_TO_ELEMENTS = { 42 | attr_to_prop(attr_name): elements 43 | for attr_name, elements in ATTR_TO_ELEMENTS.items() 44 | if attr_name not in REFLEX_PROVIDED_ATTRS 45 | } 46 | 47 | # Invert PROP_TO_ELEMENTS to enable easier lookup. 48 | ELEMENT_TO_PROPS = defaultdict(list) 49 | for prop, elements in PROP_TO_ELEMENTS.items(): 50 | for el in elements: 51 | ELEMENT_TO_PROPS[el].append(prop) 52 | -------------------------------------------------------------------------------- /reflex/components/el/element.py: -------------------------------------------------------------------------------- 1 | """Base class definition for raw HTML elements.""" 2 | 3 | from typing import ClassVar 4 | 5 | from reflex.components.component import Component 6 | 7 | 8 | class Element(Component): 9 | """The base class for all raw HTML elements.""" 10 | 11 | _is_tag_in_global_scope: ClassVar[bool] = True 12 | 13 | def __eq__(self, other: object): 14 | """Two elements are equal if they have the same tag. 15 | 16 | Args: 17 | other: The other element. 18 | 19 | Returns: 20 | True if the elements have the same tag, False otherwise. 21 | """ 22 | return isinstance(other, Element) and self.tag == other.tag 23 | -------------------------------------------------------------------------------- /reflex/components/el/elements/other.py: -------------------------------------------------------------------------------- 1 | """Other classes.""" 2 | 3 | from reflex.vars.base import Var 4 | 5 | from .base import BaseHTML 6 | 7 | 8 | class Details(BaseHTML): 9 | """Display the details element.""" 10 | 11 | tag = "details" 12 | 13 | # Indicates whether the details will be visible (expanded) to the user 14 | open: Var[bool] 15 | 16 | 17 | class Dialog(BaseHTML): 18 | """Display the dialog element.""" 19 | 20 | tag = "dialog" 21 | 22 | # Indicates whether the dialog is active and can be interacted with 23 | open: Var[bool] 24 | 25 | 26 | class Summary(BaseHTML): 27 | """Display the summary element. 28 | 29 | Used as a summary or caption for a
element. 30 | """ 31 | 32 | tag = "summary" 33 | 34 | 35 | class Slot(BaseHTML): 36 | """Display the slot element. 37 | 38 | Used as a placeholder inside a web component. 39 | """ 40 | 41 | tag = "slot" 42 | 43 | 44 | class Template(BaseHTML): 45 | """Display the template element. 46 | 47 | Used for declaring fragments of HTML that can be cloned and inserted in the document. 48 | """ 49 | 50 | tag = "template" 51 | 52 | 53 | class Math(BaseHTML): 54 | """Display the math element. 55 | 56 | Represents a mathematical expression. 57 | """ 58 | 59 | tag = "math" 60 | 61 | 62 | class Html(BaseHTML): 63 | """Display the html element.""" 64 | 65 | tag = "html" 66 | 67 | # Specifies the URL of the document's cache manifest (obsolete in HTML5) 68 | manifest: Var[str] 69 | 70 | 71 | details = Details.create 72 | dialog = Dialog.create 73 | summary = Summary.create 74 | slot = Slot.create 75 | template = Template.create 76 | math = Math.create 77 | html = Html.create 78 | -------------------------------------------------------------------------------- /reflex/components/el/elements/scripts.py: -------------------------------------------------------------------------------- 1 | """Scripts classes.""" 2 | 3 | from reflex.components.el.elements.inline import ReferrerPolicy 4 | from reflex.components.el.elements.media import CrossOrigin 5 | from reflex.vars.base import Var 6 | 7 | from .base import BaseHTML 8 | 9 | 10 | class Canvas(BaseHTML): 11 | """Display the canvas element.""" 12 | 13 | tag = "canvas" 14 | 15 | 16 | class Noscript(BaseHTML): 17 | """Display the noscript element.""" 18 | 19 | tag = "noscript" 20 | 21 | 22 | class Script(BaseHTML): 23 | """Display the script element.""" 24 | 25 | tag = "script" 26 | 27 | # Indicates that the script should be executed asynchronously 28 | async_: Var[bool] 29 | 30 | # Character encoding of the external script 31 | char_set: Var[str] 32 | 33 | # Configures the CORS requests for the script 34 | cross_origin: Var[CrossOrigin] 35 | 36 | # Indicates that the script should be executed after the page has finished parsing 37 | defer: Var[bool] 38 | 39 | # Security feature allowing browsers to verify what they fetch 40 | integrity: Var[str] 41 | 42 | # Specifies which referrer information to send when fetching the script 43 | referrer_policy: Var[ReferrerPolicy] 44 | 45 | # URL of an external script 46 | src: Var[str] 47 | 48 | # Specifies the MIME type of the script 49 | type: Var[str] 50 | 51 | 52 | canvas = Canvas.create 53 | noscript = Noscript.create 54 | script = Script.create 55 | -------------------------------------------------------------------------------- /reflex/components/el/elements/sectioning.py: -------------------------------------------------------------------------------- 1 | """Sectioning classes.""" 2 | 3 | from .base import BaseHTML 4 | 5 | 6 | class Body(BaseHTML): 7 | """Display the body element.""" 8 | 9 | tag = "body" 10 | 11 | 12 | class Address(BaseHTML): 13 | """Display the address element.""" 14 | 15 | tag = "address" 16 | 17 | 18 | class Article(BaseHTML): 19 | """Display the article element.""" 20 | 21 | tag = "article" 22 | 23 | 24 | class Aside(BaseHTML): 25 | """Display the aside element.""" 26 | 27 | tag = "aside" 28 | 29 | 30 | class Footer(BaseHTML): 31 | """Display the footer element.""" 32 | 33 | tag = "footer" 34 | 35 | 36 | class Header(BaseHTML): 37 | """Display the header element.""" 38 | 39 | tag = "header" 40 | 41 | 42 | class H1(BaseHTML): 43 | """Display the h1 element.""" 44 | 45 | tag = "h1" 46 | 47 | 48 | class H2(BaseHTML): 49 | """Display the h1 element.""" 50 | 51 | tag = "h2" 52 | 53 | 54 | class H3(BaseHTML): 55 | """Display the h1 element.""" 56 | 57 | tag = "h3" 58 | 59 | 60 | class H4(BaseHTML): 61 | """Display the h1 element.""" 62 | 63 | tag = "h4" 64 | 65 | 66 | class H5(BaseHTML): 67 | """Display the h1 element.""" 68 | 69 | tag = "h5" 70 | 71 | 72 | class H6(BaseHTML): 73 | """Display the h1 element.""" 74 | 75 | tag = "h6" 76 | 77 | 78 | class Main(BaseHTML): 79 | """Display the main element.""" 80 | 81 | tag = "main" 82 | 83 | 84 | class Nav(BaseHTML): 85 | """Display the nav element.""" 86 | 87 | tag = "nav" 88 | 89 | 90 | class Section(BaseHTML): 91 | """Display the section element.""" 92 | 93 | tag = "section" 94 | 95 | 96 | address = Address.create 97 | article = Article.create 98 | aside = Aside.create 99 | body = Body.create 100 | header = Header.create 101 | footer = Footer.create 102 | h1 = H1.create 103 | h2 = H2.create 104 | h3 = H3.create 105 | h4 = H4.create 106 | h5 = H5.create 107 | h6 = H6.create 108 | main = Main.create 109 | nav = Nav.create 110 | section = Section.create 111 | -------------------------------------------------------------------------------- /reflex/components/gridjs/__init__.py: -------------------------------------------------------------------------------- 1 | """Grid components.""" 2 | 3 | from .datatable import DataTable 4 | 5 | data_table = DataTable.create 6 | -------------------------------------------------------------------------------- /reflex/components/literals.py: -------------------------------------------------------------------------------- 1 | """Literal custom type used by Reflex.""" 2 | 3 | from typing import Literal 4 | 5 | # Base Literals 6 | LiteralInputType = Literal[ 7 | "button", 8 | "checkbox", 9 | "color", 10 | "date", 11 | "datetime-local", 12 | "email", 13 | "file", 14 | "hidden", 15 | "image", 16 | "month", 17 | "number", 18 | "password", 19 | "radio", 20 | "range", 21 | "reset", 22 | "search", 23 | "submit", 24 | "tel", 25 | "text", 26 | "time", 27 | "url", 28 | "week", 29 | ] 30 | 31 | 32 | LiteralRowMarker = Literal["none", "number", "checkbox", "both", "clickable-number"] 33 | -------------------------------------------------------------------------------- /reflex/components/lucide/__init__.py: -------------------------------------------------------------------------------- 1 | """Lucide Icon Component.""" 2 | 3 | from .icon import Icon 4 | 5 | icon = Icon.create 6 | -------------------------------------------------------------------------------- /reflex/components/markdown/__init__.py: -------------------------------------------------------------------------------- 1 | """Markdown components.""" 2 | 3 | from .markdown import Markdown 4 | 5 | markdown = Markdown.create 6 | -------------------------------------------------------------------------------- /reflex/components/moment/__init__.py: -------------------------------------------------------------------------------- 1 | """Moment.js component.""" 2 | 3 | from .moment import Moment, MomentDelta 4 | 5 | moment = Moment.create 6 | -------------------------------------------------------------------------------- /reflex/components/next/__init__.py: -------------------------------------------------------------------------------- 1 | """Namespace for components provided by next packages.""" 2 | 3 | from .base import NextComponent 4 | from .image import Image 5 | from .link import NextLink 6 | from .video import Video 7 | 8 | image = Image.create 9 | video = Video.create 10 | next_link = NextLink.create 11 | -------------------------------------------------------------------------------- /reflex/components/next/base.py: -------------------------------------------------------------------------------- 1 | """Base for NextJS components.""" 2 | 3 | from reflex.components.component import Component 4 | 5 | 6 | class NextComponent(Component): 7 | """A Component used as based for any NextJS component.""" 8 | -------------------------------------------------------------------------------- /reflex/components/next/link.py: -------------------------------------------------------------------------------- 1 | """A link component.""" 2 | 3 | from reflex.components.component import Component 4 | from reflex.vars.base import Var 5 | 6 | 7 | class NextLink(Component): 8 | """Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an .""" 9 | 10 | library = "next/link" 11 | 12 | tag = "NextLink" 13 | 14 | is_default = True 15 | 16 | # The page to link to. 17 | href: Var[str] 18 | 19 | # Whether to pass the href prop to the child. 20 | pass_href: Var[bool] = Var.create(True) 21 | -------------------------------------------------------------------------------- /reflex/components/next/video.py: -------------------------------------------------------------------------------- 1 | """Wrapping of the next-video component.""" 2 | 3 | from reflex.components.component import Component 4 | from reflex.utils import console 5 | from reflex.vars.base import Var 6 | 7 | from .base import NextComponent 8 | 9 | 10 | class Video(NextComponent): 11 | """A video component from NextJS.""" 12 | 13 | tag = "Video" 14 | library = "next-video@2.2.0" 15 | is_default = True 16 | # the URL 17 | src: Var[str] 18 | 19 | as_: Component | None 20 | 21 | @classmethod 22 | def create(cls, *children, **props) -> NextComponent: 23 | """Create a Video component. 24 | 25 | Args: 26 | *children: The children of the component. 27 | **props: The props of the component. 28 | 29 | Returns: 30 | The Video component. 31 | """ 32 | console.deprecate( 33 | "next-video", 34 | "The next-video component is deprecated. Use `rx.video` instead.", 35 | deprecation_version="0.7.11", 36 | removal_version="0.8.0", 37 | ) 38 | return super().create(*children, **props) 39 | -------------------------------------------------------------------------------- /reflex/components/plotly/__init__.py: -------------------------------------------------------------------------------- 1 | """Plotly components.""" 2 | 3 | from reflex.components.component import ComponentNamespace 4 | 5 | from .plotly import ( 6 | Plotly, 7 | PlotlyBasic, 8 | PlotlyCartesian, 9 | PlotlyFinance, 10 | PlotlyGeo, 11 | PlotlyGl2d, 12 | PlotlyGl3d, 13 | PlotlyMapbox, 14 | PlotlyStrict, 15 | ) 16 | 17 | 18 | class PlotlyNamespace(ComponentNamespace): 19 | """Plotly namespace.""" 20 | 21 | __call__ = Plotly.create 22 | basic = PlotlyBasic.create 23 | cartesian = PlotlyCartesian.create 24 | geo = PlotlyGeo.create 25 | gl2d = PlotlyGl2d.create 26 | gl3d = PlotlyGl3d.create 27 | finance = PlotlyFinance.create 28 | mapbox = PlotlyMapbox.create 29 | strict = PlotlyStrict.create 30 | 31 | 32 | plotly = PlotlyNamespace() 33 | -------------------------------------------------------------------------------- /reflex/components/radix/__init__.py: -------------------------------------------------------------------------------- 1 | """Namespace for components provided by @radix-ui packages.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex import RADIX_MAPPING 6 | from reflex.utils import lazy_loader 7 | 8 | _SUBMODULES: set[str] = {"themes", "primitives"} 9 | 10 | _SUBMOD_ATTRS: dict[str, list[str]] = { 11 | "".join(k.split("components.radix.")[-1]): v for k, v in RADIX_MAPPING.items() 12 | } 13 | __getattr__, __dir__, __all__ = lazy_loader.attach( 14 | __name__, 15 | submodules=_SUBMODULES, 16 | submod_attrs=_SUBMOD_ATTRS, 17 | ) 18 | -------------------------------------------------------------------------------- /reflex/components/radix/primitives/__init__.py: -------------------------------------------------------------------------------- 1 | """Radix primitive components (https://www.radix-ui.com/primitives).""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex import RADIX_PRIMITIVES_MAPPING 6 | from reflex.utils import lazy_loader 7 | 8 | _SUBMOD_ATTRS: dict[str, list[str]] = { 9 | "".join(k.split("components.radix.primitives.")[-1]): v 10 | for k, v in RADIX_PRIMITIVES_MAPPING.items() 11 | } 12 | 13 | __getattr__, __dir__, __all__ = lazy_loader.attach( 14 | __name__, 15 | submod_attrs=_SUBMOD_ATTRS, 16 | ) 17 | -------------------------------------------------------------------------------- /reflex/components/radix/primitives/base.py: -------------------------------------------------------------------------------- 1 | """The base component for Radix primitives.""" 2 | 3 | from reflex.components.component import Component 4 | from reflex.components.tags.tag import Tag 5 | from reflex.utils import format 6 | from reflex.vars.base import Var 7 | 8 | 9 | class RadixPrimitiveComponent(Component): 10 | """Basic component for radix Primitives.""" 11 | 12 | # Change the default rendered element for the one passed as a child. 13 | as_child: Var[bool] 14 | 15 | 16 | class RadixPrimitiveComponentWithClassName(RadixPrimitiveComponent): 17 | """Basic component for radix Primitives with a class name prop.""" 18 | 19 | def _render(self) -> Tag: 20 | return ( 21 | super() 22 | ._render() 23 | .add_props( 24 | class_name=f"{format.to_title_case(self.tag or '')} {self.class_name or ''}" 25 | ) 26 | ) 27 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/__init__.py: -------------------------------------------------------------------------------- 1 | """Namespace for components provided by the @radix-ui/themes library.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.utils import lazy_loader 6 | 7 | _SUBMODULES: set[str] = {"components", "layout", "typography"} 8 | _SUBMOD_ATTRS: dict[str, list[str]] = { 9 | "base": [ 10 | "theme", 11 | "theme_panel", 12 | ], 13 | "color_mode": [ 14 | "color_mode", 15 | ], 16 | } 17 | 18 | __getattr__, __dir__, __all__ = lazy_loader.attach( 19 | __name__, 20 | submodules=_SUBMODULES, 21 | submod_attrs=_SUBMOD_ATTRS, 22 | ) 23 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/__init__.py: -------------------------------------------------------------------------------- 1 | """Radix themes components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex import RADIX_THEMES_COMPONENTS_MAPPING 6 | from reflex.utils import lazy_loader 7 | 8 | _SUBMOD_ATTRS: dict[str, list[str]] = { 9 | "".join(k.split("components.radix.themes.components.")[-1]): v 10 | for k, v in RADIX_THEMES_COMPONENTS_MAPPING.items() 11 | } 12 | 13 | __getattr__, __dir__, __all__ = lazy_loader.attach( 14 | __name__, 15 | submod_attrs=_SUBMOD_ATTRS, 16 | ) 17 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/aspect_ratio.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from reflex.components.radix.themes.base import RadixThemesComponent 4 | from reflex.vars.base import Var 5 | 6 | 7 | class AspectRatio(RadixThemesComponent): 8 | """Displays content with a desired ratio.""" 9 | 10 | tag = "AspectRatio" 11 | 12 | # The ratio of the width to the height of the element 13 | ratio: Var[float | int] 14 | 15 | 16 | aspect_ratio = AspectRatio.create 17 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/avatar.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.radix.themes.base import ( 7 | LiteralAccentColor, 8 | LiteralRadius, 9 | RadixThemesComponent, 10 | ) 11 | from reflex.vars.base import Var 12 | 13 | LiteralSize = Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"] 14 | 15 | 16 | class Avatar(RadixThemesComponent): 17 | """An image element with a fallback for representing the user.""" 18 | 19 | tag = "Avatar" 20 | 21 | # The variant of the avatar 22 | variant: Var[Literal["solid", "soft"]] 23 | 24 | # The size of the avatar: "1" - "9" 25 | size: Var[Responsive[LiteralSize]] 26 | 27 | # Color theme of the avatar 28 | color_scheme: Var[LiteralAccentColor] 29 | 30 | # Whether to render the avatar with higher contrast color against background 31 | high_contrast: Var[bool] 32 | 33 | # Override theme radius for avatar: "none" | "small" | "medium" | "large" | "full" 34 | radius: Var[LiteralRadius] 35 | 36 | # The src of the avatar image 37 | src: Var[str] 38 | 39 | # The rendered fallback text 40 | fallback: Var[str] 41 | 42 | 43 | avatar = Avatar.create 44 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/badge.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.el import elements 7 | from reflex.components.radix.themes.base import ( 8 | LiteralAccentColor, 9 | LiteralRadius, 10 | RadixThemesComponent, 11 | ) 12 | from reflex.vars.base import Var 13 | 14 | 15 | class Badge(elements.Span, RadixThemesComponent): 16 | """A stylized badge element.""" 17 | 18 | tag = "Badge" 19 | 20 | # The variant of the badge 21 | variant: Var[Literal["solid", "soft", "surface", "outline"]] 22 | 23 | # The size of the badge 24 | size: Var[Responsive[Literal["1", "2", "3"]]] 25 | 26 | # Color theme of the badge 27 | color_scheme: Var[LiteralAccentColor] 28 | 29 | # Whether to render the badge with higher contrast color against background 30 | high_contrast: Var[bool] 31 | 32 | # Override theme radius for badge: "none" | "small" | "medium" | "large" | "full" 33 | radius: Var[LiteralRadius] 34 | 35 | 36 | badge = Badge.create 37 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/button.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.el import elements 7 | from reflex.components.radix.themes.base import ( 8 | LiteralAccentColor, 9 | LiteralRadius, 10 | LiteralVariant, 11 | RadixLoadingProp, 12 | RadixThemesComponent, 13 | ) 14 | from reflex.vars.base import Var 15 | 16 | LiteralButtonSize = Literal["1", "2", "3", "4"] 17 | 18 | 19 | class Button(elements.Button, RadixLoadingProp, RadixThemesComponent): 20 | """Trigger an action or event, such as submitting a form or displaying a dialog.""" 21 | 22 | tag = "Button" 23 | 24 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 25 | as_child: Var[bool] 26 | 27 | # Button size "1" - "4" 28 | size: Var[Responsive[LiteralButtonSize]] 29 | 30 | # Variant of button: "solid" | "soft" | "outline" | "ghost" 31 | variant: Var[LiteralVariant] 32 | 33 | # Override theme color for button 34 | color_scheme: Var[LiteralAccentColor] 35 | 36 | # Whether to render the button with higher contrast color against background 37 | high_contrast: Var[bool] 38 | 39 | # Override theme radius for button: "none" | "small" | "medium" | "large" | "full" 40 | radius: Var[LiteralRadius] 41 | 42 | 43 | button = Button.create 44 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/card.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.el import elements 7 | from reflex.components.radix.themes.base import RadixThemesComponent 8 | from reflex.vars.base import Var 9 | 10 | 11 | class Card(elements.Div, RadixThemesComponent): 12 | """Container that groups related content and actions.""" 13 | 14 | tag = "Card" 15 | 16 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 17 | as_child: Var[bool] 18 | 19 | # Card size: "1" - "5" 20 | size: Var[Responsive[Literal["1", "2", "3", "4", "5"],]] 21 | 22 | # Variant of Card: "surface" | "classic" | "ghost" 23 | variant: Var[Literal["surface", "classic", "ghost"]] 24 | 25 | 26 | card = Card.create 27 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/checkbox_cards.py: -------------------------------------------------------------------------------- 1 | """Components for the Radix CheckboxCards component.""" 2 | 3 | from types import SimpleNamespace 4 | from typing import Literal 5 | 6 | from reflex.components.core.breakpoints import Responsive 7 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 8 | from reflex.vars.base import Var 9 | 10 | 11 | class CheckboxCardsRoot(RadixThemesComponent): 12 | """Root element for a CheckboxCards component.""" 13 | 14 | tag = "CheckboxCards.Root" 15 | 16 | # The size of the checkbox cards: "1" | "2" | "3" 17 | size: Var[Responsive[Literal["1", "2", "3"]]] 18 | 19 | # Variant of button: "classic" | "surface" | "soft" 20 | variant: Var[Literal["classic", "surface"]] 21 | 22 | # Override theme color for button 23 | color_scheme: Var[LiteralAccentColor] 24 | 25 | # Uses a higher contrast color for the component. 26 | high_contrast: Var[bool] 27 | 28 | # The number of columns: 29 | columns: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] 30 | 31 | # The gap between the checkbox cards: 32 | gap: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] 33 | 34 | 35 | class CheckboxCardsItem(RadixThemesComponent): 36 | """An item in the CheckboxCards component.""" 37 | 38 | tag = "CheckboxCards.Item" 39 | 40 | 41 | class CheckboxCards(SimpleNamespace): 42 | """CheckboxCards components namespace.""" 43 | 44 | root = staticmethod(CheckboxCardsRoot.create) 45 | item = staticmethod(CheckboxCardsItem.create) 46 | 47 | 48 | checkbox_cards = CheckboxCards() 49 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/checkbox_group.py: -------------------------------------------------------------------------------- 1 | """Components for the CheckboxGroup component of Radix Themes.""" 2 | 3 | from collections.abc import Sequence 4 | from types import SimpleNamespace 5 | from typing import Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 9 | from reflex.vars.base import Var 10 | 11 | 12 | class CheckboxGroupRoot(RadixThemesComponent): 13 | """Root element for a CheckboxGroup component.""" 14 | 15 | tag = "CheckboxGroup.Root" 16 | 17 | # Use the size prop to control the checkbox size. 18 | size: Var[Responsive[Literal["1", "2", "3"]]] 19 | 20 | # Variant of button: "classic" | "surface" | "soft" 21 | variant: Var[Literal["classic", "surface", "soft"]] 22 | 23 | # Override theme color for button 24 | color_scheme: Var[LiteralAccentColor] 25 | 26 | # Uses a higher contrast color for the component. 27 | high_contrast: Var[bool] 28 | 29 | # determines which checkboxes, if any, are checked by default. 30 | default_value: Var[Sequence[str]] 31 | 32 | # used to assign a name to the entire group of checkboxes 33 | name: Var[str] 34 | 35 | 36 | class CheckboxGroupItem(RadixThemesComponent): 37 | """An item in the CheckboxGroup component.""" 38 | 39 | tag = "CheckboxGroup.Item" 40 | 41 | # specifies the value associated with a particular checkbox option. 42 | value: Var[str] 43 | 44 | # Use the native disabled attribute to create a disabled checkbox. 45 | disabled: Var[bool] 46 | 47 | 48 | class CheckboxGroup(SimpleNamespace): 49 | """CheckboxGroup components namespace.""" 50 | 51 | root = staticmethod(CheckboxGroupRoot.create) 52 | item = staticmethod(CheckboxGroupItem.create) 53 | 54 | 55 | checkbox_group = CheckboxGroup() 56 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/data_list.py: -------------------------------------------------------------------------------- 1 | """Components for the DataList component of Radix Themes.""" 2 | 3 | from types import SimpleNamespace 4 | from typing import Literal 5 | 6 | from reflex.components.core.breakpoints import Responsive 7 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 8 | from reflex.vars.base import Var 9 | 10 | 11 | class DataListRoot(RadixThemesComponent): 12 | """Root element for a DataList component.""" 13 | 14 | tag = "DataList.Root" 15 | 16 | # The orientation of the data list item: "horizontal" | "vertical" 17 | orientation: Var[Responsive[Literal["horizontal", "vertical"]]] 18 | 19 | # The size of the data list item: "1" | "2" | "3" 20 | size: Var[Responsive[Literal["1", "2", "3"]]] 21 | 22 | # Trims the leading whitespace from the start or end of the text. 23 | trim: Var[Responsive[Literal["normal", "start", "end", "both"]]] 24 | 25 | 26 | class DataListItem(RadixThemesComponent): 27 | """An item in the DataList component.""" 28 | 29 | tag = "DataList.Item" 30 | 31 | # The alignment of the data list item within its container. 32 | align: Var[Responsive[Literal["start", "center", "end", "baseline", "stretch"]]] 33 | 34 | 35 | class DataListLabel(RadixThemesComponent): 36 | """A label in the DataList component.""" 37 | 38 | tag = "DataList.Label" 39 | 40 | # The width of the component 41 | width: Var[Responsive[str]] 42 | 43 | # The minimum width of the component 44 | min_width: Var[Responsive[str]] 45 | 46 | # The maximum width of the component 47 | max_width: Var[Responsive[str]] 48 | 49 | # The color scheme for the DataList component. 50 | color_scheme: Var[LiteralAccentColor] 51 | 52 | 53 | class DataListValue(RadixThemesComponent): 54 | """A value in the DataList component.""" 55 | 56 | tag = "DataList.Value" 57 | 58 | 59 | class DataList(SimpleNamespace): 60 | """DataList components namespace.""" 61 | 62 | root = staticmethod(DataListRoot.create) 63 | item = staticmethod(DataListItem.create) 64 | label = staticmethod(DataListLabel.create) 65 | value = staticmethod(DataListValue.create) 66 | 67 | 68 | data_list = DataList() 69 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/inset.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.el import elements 7 | from reflex.components.radix.themes.base import RadixThemesComponent 8 | from reflex.vars.base import Var 9 | 10 | LiteralButtonSize = Literal["1", "2", "3", "4"] 11 | 12 | 13 | class Inset(elements.Div, RadixThemesComponent): 14 | """Applies a negative margin to allow content to bleed into the surrounding container.""" 15 | 16 | tag = "Inset" 17 | 18 | # The side 19 | side: Var[Responsive[Literal["x", "y", "top", "bottom", "right", "left"]]] 20 | 21 | # How to clip the element's content: "border-box" | "padding-box" 22 | clip: Var[Responsive[Literal["border-box", "padding-box"]]] 23 | 24 | # Padding 25 | p: Var[Responsive[int | str]] 26 | 27 | # Padding on the x axis 28 | px: Var[Responsive[int | str]] 29 | 30 | # Padding on the y axis 31 | py: Var[Responsive[int | str]] 32 | 33 | # Padding on the top 34 | pt: Var[Responsive[int | str]] 35 | 36 | # Padding on the right 37 | pr: Var[Responsive[int | str]] 38 | 39 | # Padding on the bottom 40 | pb: Var[Responsive[int | str]] 41 | 42 | # Padding on the left 43 | pl: Var[Responsive[int | str]] 44 | 45 | 46 | inset = Inset.create 47 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/radio.py: -------------------------------------------------------------------------------- 1 | """Radio component from Radix Themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 7 | from reflex.vars.base import Var 8 | 9 | 10 | class Radio(RadixThemesComponent): 11 | """A radio component.""" 12 | 13 | tag = "Radio" 14 | 15 | # The size of the radio: "1" | "2" | "3" 16 | size: Var[Responsive[Literal["1", "2", "3"]]] 17 | 18 | # Variant of button: "classic" | "surface" | "soft" 19 | variant: Var[Literal["classic", "surface", "soft"]] 20 | 21 | # Override theme color for button 22 | color_scheme: Var[LiteralAccentColor] 23 | 24 | # Uses a higher contrast color for the component. 25 | high_contrast: Var[bool] 26 | 27 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 28 | as_child = Var[bool] 29 | 30 | 31 | radio = Radio.create 32 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/scroll_area.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.radix.themes.base import RadixThemesComponent 6 | from reflex.vars.base import Var 7 | 8 | 9 | class ScrollArea(RadixThemesComponent): 10 | """Custom styled, cross-browser scrollable area using native functionality.""" 11 | 12 | tag = "ScrollArea" 13 | 14 | # The alignment of the scroll area 15 | scrollbars: Var[Literal["vertical", "horizontal", "both"]] 16 | 17 | # Describes the nature of scrollbar visibility, similar to how the scrollbar preferences in MacOS control visibility of native scrollbars. "auto" | "always" | "scroll" | "hover" 18 | type: Var[Literal["auto", "always", "scroll", "hover"]] 19 | 20 | # 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. 21 | scroll_hide_delay: Var[int] 22 | 23 | 24 | scroll_area = ScrollArea.create 25 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/separator.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 7 | from reflex.vars.base import LiteralVar, Var 8 | 9 | LiteralSeperatorSize = Literal["1", "2", "3", "4"] 10 | 11 | 12 | class Separator(RadixThemesComponent): 13 | """Visually or semantically separates content.""" 14 | 15 | tag = "Separator" 16 | 17 | # The size of the select: "1" | "2" | "3" | "4" 18 | size: Var[Responsive[LiteralSeperatorSize]] = LiteralVar.create("4") 19 | 20 | # The color of the select 21 | color_scheme: Var[LiteralAccentColor] 22 | 23 | # The orientation of the separator. 24 | orientation: Var[Responsive[Literal["horizontal", "vertical"]]] 25 | 26 | # When true, signifies that it is purely visual, carries no semantic meaning, and ensures it is not present in the accessibility tree. 27 | decorative: Var[bool] 28 | 29 | 30 | # Alias to divider. 31 | divider = separator = Separator.create 32 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/skeleton.py: -------------------------------------------------------------------------------- 1 | """Skeleton theme from Radix components.""" 2 | 3 | from reflex.components.core.breakpoints import Responsive 4 | from reflex.components.radix.themes.base import RadixLoadingProp, RadixThemesComponent 5 | from reflex.constants.compiler import MemoizationMode 6 | from reflex.vars.base import Var 7 | 8 | 9 | class Skeleton(RadixLoadingProp, RadixThemesComponent): 10 | """Skeleton component.""" 11 | 12 | tag = "Skeleton" 13 | 14 | # The width of the skeleton 15 | width: Var[Responsive[str]] 16 | 17 | # The minimum width of the skeleton 18 | min_width: Var[Responsive[str]] 19 | 20 | # The maximum width of the skeleton 21 | max_width: Var[Responsive[str]] 22 | 23 | # The height of the skeleton 24 | height: Var[Responsive[str]] 25 | 26 | # The minimum height of the skeleton 27 | min_height: Var[Responsive[str]] 28 | 29 | # The maximum height of the skeleton 30 | max_height: Var[Responsive[str]] 31 | 32 | _memoization_mode = MemoizationMode(recursive=False) 33 | 34 | 35 | skeleton = Skeleton.create 36 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/spinner.py: -------------------------------------------------------------------------------- 1 | """Radix Spinner Component.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.radix.themes.base import RadixLoadingProp, RadixThemesComponent 7 | from reflex.vars.base import Var 8 | 9 | LiteralSpinnerSize = Literal["1", "2", "3"] 10 | 11 | 12 | class Spinner(RadixLoadingProp, RadixThemesComponent): 13 | """A spinner component.""" 14 | 15 | tag = "Spinner" 16 | 17 | is_default = False 18 | 19 | # The size of the spinner. 20 | size: Var[Responsive[LiteralSpinnerSize]] 21 | 22 | 23 | spinner = Spinner.create 24 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/components/switch.py: -------------------------------------------------------------------------------- 1 | """Interactive components provided by @radix-ui/themes.""" 2 | 3 | from typing import Literal 4 | 5 | from reflex.components.core.breakpoints import Responsive 6 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 7 | from reflex.event import EventHandler, passthrough_event_spec 8 | from reflex.vars.base import Var 9 | 10 | LiteralSwitchSize = Literal["1", "2", "3"] 11 | 12 | 13 | class Switch(RadixThemesComponent): 14 | """A toggle switch alternative to the checkbox.""" 15 | 16 | tag = "Switch" 17 | 18 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 19 | as_child: Var[bool] 20 | 21 | # Whether the switch is checked by default 22 | default_checked: Var[bool] 23 | 24 | # Whether the switch is checked 25 | checked: Var[bool] 26 | 27 | # If true, prevent the user from interacting with the switch 28 | disabled: Var[bool] 29 | 30 | # If true, the user must interact with the switch to submit the form 31 | required: Var[bool] 32 | 33 | # The name of the switch (when submitting a form) 34 | name: Var[str] 35 | 36 | # The value associated with the "on" position 37 | value: Var[str] 38 | 39 | # Switch size "1" - "4" 40 | size: Var[Responsive[LiteralSwitchSize]] 41 | 42 | # Variant of switch: "classic" | "surface" | "soft" 43 | variant: Var[Literal["classic", "surface", "soft"]] 44 | 45 | # Override theme color for switch 46 | color_scheme: Var[LiteralAccentColor] 47 | 48 | # Whether to render the switch with higher contrast color against background 49 | high_contrast: Var[bool] 50 | 51 | # Override theme radius for switch: "none" | "small" | "full" 52 | radius: Var[Literal["none", "small", "full"]] 53 | 54 | # Props to rename 55 | _rename_props = {"onChange": "onCheckedChange"} 56 | 57 | # Fired when the value of the switch changes 58 | on_change: EventHandler[passthrough_event_spec(bool)] 59 | 60 | 61 | switch = Switch.create 62 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/__init__.py: -------------------------------------------------------------------------------- 1 | """Layout components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex import RADIX_THEMES_LAYOUT_MAPPING 6 | from reflex.utils import lazy_loader 7 | 8 | _SUBMOD_ATTRS: dict[str, list[str]] = { 9 | "".join(k.split("components.radix.themes.layout.")[-1]): v 10 | for k, v in RADIX_THEMES_LAYOUT_MAPPING.items() 11 | } 12 | 13 | 14 | __getattr__, __dir__, __all__ = lazy_loader.attach( 15 | __name__, 16 | submod_attrs=_SUBMOD_ATTRS, 17 | ) 18 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/base.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.radix.themes.base import ( 9 | CommonMarginProps, 10 | CommonPaddingProps, 11 | RadixThemesComponent, 12 | ) 13 | from reflex.vars.base import Var 14 | 15 | LiteralBoolNumber = Literal["0", "1"] 16 | 17 | 18 | class LayoutComponent(CommonMarginProps, CommonPaddingProps, RadixThemesComponent): 19 | """Box, Flex and Grid are foundational elements you'll use to construct 20 | layouts. Box provides block-level spacing and sizing, while Flex and Grid 21 | let you create flexible columns, rows and grids. 22 | """ 23 | 24 | # Whether the element will take up the smallest possible space: "0" | "1" 25 | flex_shrink: Var[Responsive[LiteralBoolNumber]] 26 | 27 | # Whether the element will take up the largest possible space: "0" | "1" 28 | flex_grow: Var[Responsive[LiteralBoolNumber]] 29 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/box.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.components.el import elements 6 | from reflex.components.radix.themes.base import RadixThemesComponent 7 | 8 | 9 | class Box(elements.Div, RadixThemesComponent): 10 | """A fundamental layout building block, based on `div` element.""" 11 | 12 | tag = "Box" 13 | 14 | 15 | box = Box.create 16 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/center.py: -------------------------------------------------------------------------------- 1 | """A center component.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Any 6 | 7 | from .flex import Flex 8 | 9 | 10 | class Center(Flex): 11 | """A center component.""" 12 | 13 | def add_style(self) -> dict[str, Any] | None: 14 | """Add style that center the content. 15 | 16 | Returns: 17 | The style of the component. 18 | """ 19 | return { 20 | "display": "flex", 21 | "align_items": "center", 22 | "justify_content": "center", 23 | } 24 | 25 | 26 | center = Center.create 27 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/container.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.el import elements 9 | from reflex.components.radix.themes.base import RadixThemesComponent 10 | from reflex.style import STACK_CHILDREN_FULL_WIDTH 11 | from reflex.vars.base import LiteralVar, Var 12 | 13 | LiteralContainerSize = Literal["1", "2", "3", "4"] 14 | 15 | 16 | class Container(elements.Div, RadixThemesComponent): 17 | """Constrains the maximum width of page content. 18 | 19 | See https://www.radix-ui.com/themes/docs/components/container 20 | """ 21 | 22 | tag = "Container" 23 | 24 | # The size of the container: "1" - "4" (default "3") 25 | size: Var[Responsive[LiteralContainerSize]] = LiteralVar.create("3") 26 | 27 | @classmethod 28 | def create( 29 | cls, 30 | *children, 31 | padding: str = "16px", 32 | stack_children_full_width: bool = False, 33 | **props, 34 | ): 35 | """Create the container component. 36 | 37 | Args: 38 | children: The children components. 39 | padding: The padding of the container. 40 | stack_children_full_width: If True, any vstack/hstack children will have 100% width. 41 | props: The properties of the container. 42 | 43 | Returns: 44 | The container component. 45 | """ 46 | if stack_children_full_width: 47 | props["style"] = {**STACK_CHILDREN_FULL_WIDTH, **props.pop("style", {})} 48 | return super().create( 49 | *children, 50 | padding=padding, 51 | **props, 52 | ) 53 | 54 | 55 | container = Container.create 56 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/flex.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import ClassVar, Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.el import elements 9 | from reflex.components.radix.themes.base import ( 10 | LiteralAlign, 11 | LiteralJustify, 12 | LiteralSpacing, 13 | RadixThemesComponent, 14 | ) 15 | from reflex.vars.base import Var 16 | 17 | LiteralFlexDirection = Literal["row", "column", "row-reverse", "column-reverse"] 18 | LiteralFlexWrap = Literal["nowrap", "wrap", "wrap-reverse"] 19 | 20 | 21 | class Flex(elements.Div, RadixThemesComponent): 22 | """Component for creating flex layouts.""" 23 | 24 | tag = "Flex" 25 | 26 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 27 | as_child: Var[bool] 28 | 29 | # How child items are laid out: "row" | "column" | "row-reverse" | "column-reverse" 30 | direction: Var[Responsive[LiteralFlexDirection]] 31 | 32 | # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" 33 | align: Var[Responsive[LiteralAlign]] 34 | 35 | # Alignment of children along the cross axis: "start" | "center" | "end" | "between" 36 | justify: Var[Responsive[LiteralJustify]] 37 | 38 | # Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" 39 | wrap: Var[Responsive[LiteralFlexWrap]] 40 | 41 | # Gap between children: "0" - "9" 42 | spacing: Var[Responsive[LiteralSpacing]] 43 | 44 | # Reflex maps the "spacing" prop to "gap" prop. 45 | _rename_props: ClassVar[dict[str, str]] = {"spacing": "gap"} 46 | 47 | 48 | flex = Flex.create 49 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/grid.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import ClassVar, Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.el import elements 9 | from reflex.components.radix.themes.base import ( 10 | LiteralAlign, 11 | LiteralJustify, 12 | LiteralSpacing, 13 | RadixThemesComponent, 14 | ) 15 | from reflex.vars.base import Var 16 | 17 | LiteralGridFlow = Literal["row", "column", "dense", "row-dense", "column-dense"] 18 | 19 | 20 | class Grid(elements.Div, RadixThemesComponent): 21 | """Component for creating grid layouts.""" 22 | 23 | tag = "Grid" 24 | 25 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 26 | as_child: Var[bool] 27 | 28 | # Number of columns 29 | columns: Var[Responsive[str]] 30 | 31 | # Number of rows 32 | rows: Var[Responsive[str]] 33 | 34 | # How the grid items are laid out: "row" | "column" | "dense" | "row-dense" | "column-dense" 35 | flow: Var[Responsive[LiteralGridFlow]] 36 | 37 | # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" 38 | align: Var[Responsive[LiteralAlign]] 39 | 40 | # Alignment of children along the cross axis: "start" | "center" | "end" | "between" 41 | justify: Var[Responsive[LiteralJustify]] 42 | 43 | # Gap between children: "0" - "9" 44 | spacing: Var[Responsive[LiteralSpacing]] 45 | 46 | # Gap between children horizontal: "0" - "9" 47 | spacing_x: Var[Responsive[LiteralSpacing]] 48 | 49 | # Gap between children vertical: "0" - "9" 50 | spacing_y: Var[Responsive[LiteralSpacing]] 51 | 52 | # Reflex maps the "spacing" prop to "gap" prop. 53 | _rename_props: ClassVar[dict[str, str]] = { 54 | "spacing": "gap", 55 | "spacing_x": "gap_x", 56 | "spacing_y": "gap_y", 57 | } 58 | 59 | 60 | grid = Grid.create 61 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/section.py: -------------------------------------------------------------------------------- 1 | """Declarative layout and common spacing props.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Literal 6 | 7 | from reflex.components.core.breakpoints import Responsive 8 | from reflex.components.el import elements 9 | from reflex.components.radix.themes.base import RadixThemesComponent 10 | from reflex.vars.base import LiteralVar, Var 11 | 12 | LiteralSectionSize = Literal["1", "2", "3"] 13 | 14 | 15 | class Section(elements.Section, RadixThemesComponent): 16 | """Denotes a section of page content.""" 17 | 18 | tag = "Section" 19 | 20 | # The size of the section: "1" - "3" (default "2") 21 | size: Var[Responsive[LiteralSectionSize]] = LiteralVar.create("2") 22 | 23 | 24 | section = Section.create 25 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/spacer.py: -------------------------------------------------------------------------------- 1 | """A spacer component.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import Any 6 | 7 | from .flex import Flex 8 | 9 | 10 | class Spacer(Flex): 11 | """A spacer component.""" 12 | 13 | def add_style(self) -> dict[str, Any] | None: 14 | """Add style to the component. 15 | 16 | Returns: 17 | The style of the component. 18 | """ 19 | return { 20 | "flex": 1, 21 | "justify_self": "stretch", 22 | "align_self": "stretch", 23 | } 24 | 25 | 26 | spacer = Spacer.create 27 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/layout/stack.py: -------------------------------------------------------------------------------- 1 | """Stack components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex.components.component import Component 6 | from reflex.components.core.breakpoints import Responsive 7 | from reflex.components.radix.themes.base import LiteralAlign, LiteralSpacing 8 | from reflex.vars.base import Var 9 | 10 | from .flex import Flex, LiteralFlexDirection 11 | 12 | 13 | class Stack(Flex): 14 | """A stack component.""" 15 | 16 | # The spacing between each stack item. 17 | spacing: Var[Responsive[LiteralSpacing]] = Var.create("3") 18 | 19 | # The alignment of the stack items. 20 | align: Var[Responsive[LiteralAlign]] = Var.create("start") 21 | 22 | @classmethod 23 | def create( 24 | cls, 25 | *children, 26 | **props, 27 | ) -> Component: 28 | """Create a new instance of the component. 29 | 30 | Args: 31 | *children: The children of the stack. 32 | **props: The properties of the stack. 33 | 34 | Returns: 35 | The stack component. 36 | """ 37 | # Apply the default classname 38 | given_class_name = props.pop("class_name", []) 39 | if not isinstance(given_class_name, list): 40 | given_class_name = [given_class_name] 41 | props["class_name"] = ["rx-Stack", *given_class_name] 42 | 43 | return super().create( 44 | *children, 45 | **props, 46 | ) 47 | 48 | 49 | class VStack(Stack): 50 | """A vertical stack component.""" 51 | 52 | # The direction of the stack. 53 | direction: Var[Responsive[LiteralFlexDirection]] = Var.create("column") 54 | 55 | 56 | class HStack(Stack): 57 | """A horizontal stack component.""" 58 | 59 | # The direction of the stack. 60 | direction: Var[Responsive[LiteralFlexDirection]] = Var.create("row") 61 | 62 | 63 | stack = Stack.create 64 | hstack = HStack.create 65 | vstack = VStack.create 66 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/typography/__init__.py: -------------------------------------------------------------------------------- 1 | """Typographic components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from reflex import RADIX_THEMES_TYPOGRAPHY_MAPPING 6 | from reflex.utils import lazy_loader 7 | 8 | _SUBMOD_ATTRS: dict[str, list[str]] = { 9 | "".join(k.split("components.radix.themes.typography.")[-1]): v 10 | for k, v in RADIX_THEMES_TYPOGRAPHY_MAPPING.items() 11 | } 12 | 13 | __getattr__, __dir__, __all__ = lazy_loader.attach( 14 | __name__, 15 | submod_attrs=_SUBMOD_ATTRS, 16 | ) 17 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/typography/base.py: -------------------------------------------------------------------------------- 1 | """Components for rendering text. 2 | 3 | https://www.radix-ui.com/themes/docs/theme/typography 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | from typing import Literal 9 | 10 | LiteralTextWeight = Literal["light", "regular", "medium", "bold"] 11 | LiteralTextAlign = Literal["left", "center", "right"] 12 | LiteralTextSize = Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"] 13 | LiteralTextTrim = Literal["normal", "start", "end", "both"] 14 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/typography/blockquote.py: -------------------------------------------------------------------------------- 1 | """Components for rendering heading. 2 | 3 | https://www.radix-ui.com/themes/docs/theme/typography 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | from reflex.components.core.breakpoints import Responsive 9 | from reflex.components.el import elements 10 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 11 | from reflex.vars.base import Var 12 | 13 | from .base import LiteralTextSize, LiteralTextWeight 14 | 15 | 16 | class Blockquote(elements.Blockquote, RadixThemesComponent): 17 | """A block level extended quotation.""" 18 | 19 | tag = "Blockquote" 20 | 21 | # Text size: "1" - "9" 22 | size: Var[Responsive[LiteralTextSize]] 23 | 24 | # Thickness of text: "light" | "regular" | "medium" | "bold" 25 | weight: Var[Responsive[LiteralTextWeight]] 26 | 27 | # Overrides the accent color inherited from the Theme. 28 | color_scheme: Var[LiteralAccentColor] 29 | 30 | # Whether to render the text with higher contrast color 31 | high_contrast: Var[bool] 32 | 33 | 34 | blockquote = Blockquote.create 35 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/typography/code.py: -------------------------------------------------------------------------------- 1 | """Components for rendering heading. 2 | 3 | https://www.radix-ui.com/themes/docs/theme/typography 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | from reflex.components.core.breakpoints import Responsive 9 | from reflex.components.el import elements 10 | from reflex.components.markdown.markdown import MarkdownComponentMap 11 | from reflex.components.radix.themes.base import ( 12 | LiteralAccentColor, 13 | LiteralVariant, 14 | RadixThemesComponent, 15 | ) 16 | from reflex.vars.base import Var 17 | 18 | from .base import LiteralTextSize, LiteralTextWeight 19 | 20 | 21 | class Code(elements.Code, RadixThemesComponent, MarkdownComponentMap): 22 | """A block level extended quotation.""" 23 | 24 | tag = "Code" 25 | 26 | # The visual variant to apply: "solid" | "soft" | "outline" | "ghost" 27 | variant: Var[LiteralVariant] 28 | 29 | # Text size: "1" - "9" 30 | size: Var[Responsive[LiteralTextSize]] 31 | 32 | # Thickness of text: "light" | "regular" | "medium" | "bold" 33 | weight: Var[Responsive[LiteralTextWeight]] 34 | 35 | # Overrides the accent color inherited from the Theme. 36 | color_scheme: Var[LiteralAccentColor] 37 | 38 | # Whether to render the text with higher contrast color 39 | high_contrast: Var[bool] 40 | 41 | 42 | code = Code.create 43 | -------------------------------------------------------------------------------- /reflex/components/radix/themes/typography/heading.py: -------------------------------------------------------------------------------- 1 | """Components for rendering heading. 2 | 3 | https://www.radix-ui.com/themes/docs/theme/typography 4 | """ 5 | 6 | from __future__ import annotations 7 | 8 | from reflex.components.core.breakpoints import Responsive 9 | from reflex.components.el import elements 10 | from reflex.components.markdown.markdown import MarkdownComponentMap 11 | from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent 12 | from reflex.vars.base import Var 13 | 14 | from .base import LiteralTextAlign, LiteralTextSize, LiteralTextTrim, LiteralTextWeight 15 | 16 | 17 | class Heading(elements.H1, RadixThemesComponent, MarkdownComponentMap): 18 | """A foundational text primitive based on the element.""" 19 | 20 | tag = "Heading" 21 | 22 | # Change the default rendered element for the one passed as a child, merging their props and behavior. 23 | as_child: Var[bool] 24 | 25 | # Change the default rendered element into a semantically appropriate alternative (cannot be used with asChild) 26 | as_: Var[str] 27 | 28 | # Text size: "1" - "9" 29 | size: Var[Responsive[LiteralTextSize]] 30 | 31 | # Thickness of text: "light" | "regular" | "medium" | "bold" 32 | weight: Var[Responsive[LiteralTextWeight]] 33 | 34 | # Alignment of text in element: "left" | "center" | "right" 35 | align: Var[Responsive[LiteralTextAlign]] 36 | 37 | # Removes the leading trim space: "normal" | "start" | "end" | "both" 38 | trim: Var[Responsive[LiteralTextTrim]] 39 | 40 | # Overrides the accent color inherited from the Theme. 41 | color_scheme: Var[LiteralAccentColor] 42 | 43 | # Whether to render the text with higher contrast color 44 | high_contrast: Var[bool] 45 | 46 | 47 | heading = Heading.create 48 | -------------------------------------------------------------------------------- /reflex/components/react_player/__init__.py: -------------------------------------------------------------------------------- 1 | """React Player component for audio and video.""" 2 | 3 | from . import react_player 4 | from .audio import Audio 5 | from .video import Video 6 | 7 | audio = Audio.create 8 | video = Video.create 9 | -------------------------------------------------------------------------------- /reflex/components/react_player/audio.py: -------------------------------------------------------------------------------- 1 | """A audio component.""" 2 | 3 | from reflex.components.react_player.react_player import ReactPlayer 4 | 5 | 6 | class Audio(ReactPlayer): 7 | """Audio component share with Video component.""" 8 | -------------------------------------------------------------------------------- /reflex/components/react_player/video.py: -------------------------------------------------------------------------------- 1 | """A video component.""" 2 | 3 | from reflex.components.react_player.react_player import ReactPlayer 4 | 5 | 6 | class Video(ReactPlayer): 7 | """Video component share with audio component.""" 8 | -------------------------------------------------------------------------------- /reflex/components/sonner/__init__.py: -------------------------------------------------------------------------------- 1 | """Init file for the sonner component.""" 2 | 3 | from .toast import toast 4 | -------------------------------------------------------------------------------- /reflex/components/suneditor/__init__.py: -------------------------------------------------------------------------------- 1 | """Editor component.""" 2 | 3 | from .editor import Editor, EditorButtonList, EditorOptions 4 | 5 | editor = Editor.create 6 | -------------------------------------------------------------------------------- /reflex/components/tags/__init__.py: -------------------------------------------------------------------------------- 1 | """Representations for React tags.""" 2 | 3 | from .cond_tag import CondTag 4 | from .iter_tag import IterTag 5 | from .match_tag import MatchTag 6 | from .tag import Tag 7 | -------------------------------------------------------------------------------- /reflex/components/tags/cond_tag.py: -------------------------------------------------------------------------------- 1 | """Tag to conditionally render components.""" 2 | 3 | import dataclasses 4 | from typing import Any 5 | 6 | from reflex.components.tags.tag import Tag 7 | from reflex.vars.base import Var 8 | 9 | 10 | @dataclasses.dataclass() 11 | class CondTag(Tag): 12 | """A conditional tag.""" 13 | 14 | # The condition to determine which component to render. 15 | cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True)) 16 | 17 | # The code to render if the condition is true. 18 | true_value: dict = dataclasses.field(default_factory=dict) 19 | 20 | # The code to render if the condition is false. 21 | false_value: dict | None = None 22 | -------------------------------------------------------------------------------- /reflex/components/tags/match_tag.py: -------------------------------------------------------------------------------- 1 | """Tag to conditionally match cases.""" 2 | 3 | import dataclasses 4 | from typing import Any 5 | 6 | from reflex.components.tags.tag import Tag 7 | from reflex.vars.base import Var 8 | 9 | 10 | @dataclasses.dataclass() 11 | class MatchTag(Tag): 12 | """A match tag.""" 13 | 14 | # The condition to determine which case to match. 15 | cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True)) 16 | 17 | # The list of match cases to be matched. 18 | match_cases: list[Any] = dataclasses.field(default_factory=list) 19 | 20 | # The catchall case to match. 21 | default: Any = dataclasses.field(default=Var.create(None)) 22 | -------------------------------------------------------------------------------- /reflex/components/tags/tagless.py: -------------------------------------------------------------------------------- 1 | """A tag with no tag.""" 2 | 3 | from reflex.components.tags import Tag 4 | from reflex.utils import format 5 | 6 | 7 | class Tagless(Tag): 8 | """A tag with no tag.""" 9 | 10 | def __str__(self) -> str: 11 | """Return the string representation of the tag. 12 | 13 | Returns: 14 | The string representation of the tag. 15 | """ 16 | out = self.contents 17 | space = format.wrap(" ", "{") 18 | if len(self.contents) > 0 and self.contents[0] == " ": 19 | out = space + out 20 | if len(self.contents) > 0 and self.contents[-1] == " ": 21 | out = out + space 22 | return out 23 | -------------------------------------------------------------------------------- /reflex/constants/config.py: -------------------------------------------------------------------------------- 1 | """Config constants.""" 2 | 3 | from pathlib import Path 4 | from types import SimpleNamespace 5 | 6 | from reflex.constants.base import Dirs, Reflex 7 | 8 | from .compiler import Ext 9 | 10 | # Alembic migrations 11 | ALEMBIC_CONFIG = "alembic.ini" 12 | 13 | 14 | class Config(SimpleNamespace): 15 | """Config constants.""" 16 | 17 | # The name of the reflex config module. 18 | MODULE = "rxconfig" 19 | # The python config file. 20 | FILE = Path(f"{MODULE}{Ext.PY}") 21 | 22 | 23 | class Expiration(SimpleNamespace): 24 | """Expiration constants.""" 25 | 26 | # Token expiration time in seconds 27 | TOKEN = 60 * 60 28 | # Maximum time in milliseconds that a state can be locked for exclusive access. 29 | LOCK = 10000 30 | # The PING timeout 31 | PING = 120 32 | # The maximum time in milliseconds to hold a lock before throwing a warning. 33 | LOCK_WARNING_THRESHOLD = 1000 34 | 35 | 36 | class GitIgnore(SimpleNamespace): 37 | """Gitignore constants.""" 38 | 39 | # The gitignore file. 40 | FILE = Path(".gitignore") 41 | # Files to gitignore. 42 | DEFAULTS = { 43 | Dirs.WEB, 44 | Dirs.STATES, 45 | "*.db", 46 | "__pycache__/", 47 | "*.py[cod]", 48 | "assets/external/", 49 | } 50 | 51 | 52 | class PyprojectToml(SimpleNamespace): 53 | """Pyproject.toml constants.""" 54 | 55 | # The pyproject.toml file. 56 | FILE = "pyproject.toml" 57 | 58 | 59 | class RequirementsTxt(SimpleNamespace): 60 | """Requirements.txt constants.""" 61 | 62 | # The requirements.txt file. 63 | FILE = "requirements.txt" 64 | # The partial text used to form requirement that pins a reflex version 65 | DEFAULTS_STUB = f"{Reflex.MODULE_NAME}==" 66 | 67 | 68 | class DefaultPorts(SimpleNamespace): 69 | """Default port constants.""" 70 | 71 | FRONTEND_PORT = 3000 72 | BACKEND_PORT = 8000 73 | 74 | 75 | # The deployment URL. 76 | PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app" 77 | -------------------------------------------------------------------------------- /reflex/constants/custom_components.py: -------------------------------------------------------------------------------- 1 | """Constants for the custom components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from pathlib import Path 6 | from types import SimpleNamespace 7 | 8 | 9 | class CustomComponents(SimpleNamespace): 10 | """Constants for the custom components.""" 11 | 12 | # The name of the custom components source directory. 13 | SRC_DIR = Path("custom_components") 14 | # The name of the custom components pyproject.toml file. 15 | PYPROJECT_TOML = Path("pyproject.toml") 16 | # The name of the custom components package README file. 17 | PACKAGE_README = Path("README.md") 18 | # The name of the custom components package .gitignore file. 19 | PACKAGE_GITIGNORE = ".gitignore" 20 | # The name of the distribution directory as result of a build. 21 | DIST_DIR = "dist" 22 | # The name of the init file. 23 | INIT_FILE = "__init__.py" 24 | # Suffixes for the distribution files. 25 | DISTRIBUTION_FILE_SUFFIXES = [".tar.gz", ".whl"] 26 | # The name to the URL of python package repositories. 27 | REPO_URLS = { 28 | # Note: the trailing slash is required for below URLs. 29 | "pypi": "https://upload.pypi.org/legacy/", 30 | "testpypi": "https://test.pypi.org/legacy/", 31 | } 32 | # The .gitignore file for the custom component project. 33 | FILE = Path(".gitignore") 34 | # Files to gitignore. 35 | DEFAULTS = {"__pycache__/", "*.py[cod]", "*.egg-info/", "dist/"} 36 | -------------------------------------------------------------------------------- /reflex/constants/state.py: -------------------------------------------------------------------------------- 1 | """State-related constants.""" 2 | 3 | from enum import Enum 4 | 5 | 6 | class StateManagerMode(str, Enum): 7 | """State manager constants.""" 8 | 9 | DISK = "disk" 10 | MEMORY = "memory" 11 | REDIS = "redis" 12 | 13 | 14 | # Used for things like console_log, etc. 15 | FRONTEND_EVENT_STATE = "__reflex_internal_frontend_event_state" 16 | -------------------------------------------------------------------------------- /reflex/constants/utils.py: -------------------------------------------------------------------------------- 1 | """Utility functions for constants.""" 2 | 3 | from collections.abc import Callable 4 | from typing import Any, Generic, TypeVar 5 | 6 | T = TypeVar("T") 7 | V = TypeVar("V") 8 | 9 | 10 | class classproperty(Generic[T, V]): 11 | """A class property decorator.""" 12 | 13 | def __init__(self, getter: Callable[[type[T]], V]) -> None: 14 | """Initialize the class property. 15 | 16 | Args: 17 | getter: The getter function. 18 | """ 19 | self.getter = getattr(getter, "__func__", getter) 20 | 21 | def __get__(self, instance: Any, owner: type[T]) -> V: 22 | """Get the value of the class property. 23 | 24 | Args: 25 | instance: The instance of the class. 26 | owner: The class itself. 27 | 28 | Returns: 29 | The value of the class property. 30 | """ 31 | return self.getter(owner) 32 | -------------------------------------------------------------------------------- /reflex/custom_components/__init__.py: -------------------------------------------------------------------------------- 1 | """The Reflex custom components.""" 2 | -------------------------------------------------------------------------------- /reflex/experimental/__init__.py: -------------------------------------------------------------------------------- 1 | """Namespace for experimental features.""" 2 | 3 | from types import SimpleNamespace 4 | 5 | from reflex.components.datadisplay.shiki_code_block import code_block as code_block 6 | from reflex.utils.console import warn 7 | from reflex.utils.misc import run_in_thread 8 | 9 | from . import hooks as hooks 10 | from .client_state import ClientStateVar as ClientStateVar 11 | 12 | 13 | class ExperimentalNamespace(SimpleNamespace): 14 | """Namespace for experimental features.""" 15 | 16 | def __getattribute__(self, item: str): 17 | """Get attribute from the namespace. 18 | 19 | Args: 20 | item: attribute name. 21 | 22 | Returns: 23 | The attribute. 24 | """ 25 | warn( 26 | "`rx._x` contains experimental features and might be removed at any time in the future.", 27 | dedupe=True, 28 | ) 29 | return super().__getattribute__(item) 30 | 31 | @property 32 | def run_in_thread(self): 33 | """Temporary property returning the run_in_thread helper function. 34 | 35 | Remove this property when run_in_thread is fully promoted. 36 | 37 | Returns: 38 | The run_in_thread helper function. 39 | """ 40 | self.register_component_warning("run_in_thread") 41 | return run_in_thread 42 | 43 | @staticmethod 44 | def register_component_warning(component_name: str): 45 | """Add component to emitted warnings and throw a warning if it 46 | doesn't exist. 47 | 48 | Args: 49 | component_name: name of the component. 50 | """ 51 | warn( 52 | f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.", 53 | dedupe=True, 54 | ) 55 | 56 | 57 | _x = ExperimentalNamespace( 58 | client_state=ClientStateVar.create, 59 | hooks=hooks, 60 | code_block=code_block, 61 | ) 62 | -------------------------------------------------------------------------------- /reflex/istate/__init__.py: -------------------------------------------------------------------------------- 1 | """This module will provide interfaces for the state.""" 2 | -------------------------------------------------------------------------------- /reflex/istate/dynamic.py: -------------------------------------------------------------------------------- 1 | """A container for dynamically generated states.""" 2 | 3 | # This page intentionally left blank. 4 | -------------------------------------------------------------------------------- /reflex/istate/wrappers.py: -------------------------------------------------------------------------------- 1 | """Wrappers for the state manager.""" 2 | 3 | from typing import Any 4 | 5 | from reflex.istate.proxy import ReadOnlyStateProxy 6 | from reflex.state import _split_substate_key, _substate_key, get_state_manager 7 | 8 | 9 | async def get_state(token: str, state_cls: Any | None = None) -> ReadOnlyStateProxy: 10 | """Get the instance of a state for a token. 11 | 12 | Args: 13 | token: The token for the state. 14 | state_cls: The class of the state. 15 | 16 | Returns: 17 | A read-only proxy of the state instance. 18 | """ 19 | mng = get_state_manager() 20 | if state_cls is not None: 21 | root_state = await mng.get_state(_substate_key(token, state_cls)) 22 | else: 23 | root_state = await mng.get_state(token) 24 | _, state_path = _split_substate_key(token) 25 | state_cls = root_state.get_class_substate(tuple(state_path.split("."))) 26 | instance = await root_state.get_state(state_cls) 27 | return ReadOnlyStateProxy(instance) 28 | -------------------------------------------------------------------------------- /reflex/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | """Reflex middleware.""" 2 | 3 | from .hydrate_middleware import HydrateMiddleware 4 | from .middleware import Middleware 5 | -------------------------------------------------------------------------------- /reflex/middleware/hydrate_middleware.py: -------------------------------------------------------------------------------- 1 | """Middleware to hydrate the state.""" 2 | 3 | from __future__ import annotations 4 | 5 | import dataclasses 6 | from typing import TYPE_CHECKING 7 | 8 | from reflex import constants 9 | from reflex.event import Event, get_hydrate_event 10 | from reflex.middleware.middleware import Middleware 11 | from reflex.state import BaseState, StateUpdate, _resolve_delta 12 | 13 | if TYPE_CHECKING: 14 | from reflex.app import App 15 | 16 | 17 | @dataclasses.dataclass(init=True) 18 | class HydrateMiddleware(Middleware): 19 | """Middleware to handle initial app hydration.""" 20 | 21 | async def preprocess( 22 | self, app: App, state: BaseState, event: Event 23 | ) -> StateUpdate | None: 24 | """Preprocess the event. 25 | 26 | Args: 27 | app: The app to apply the middleware to. 28 | state: The client state. 29 | event: The event to preprocess. 30 | 31 | Returns: 32 | An optional delta or list of state updates to return. 33 | """ 34 | # If this is not the hydrate event, return None 35 | if event.name != get_hydrate_event(state): 36 | return None 37 | 38 | # Clear client storage, to respect clearing cookies 39 | state._reset_client_storage() 40 | 41 | # Mark state as not hydrated (until on_loads are complete) 42 | setattr(state, constants.CompileVars.IS_HYDRATED, False) 43 | 44 | # Get the initial state. 45 | delta = await _resolve_delta(state.dict()) 46 | # since a full dict was captured, clean any dirtiness 47 | state._clean() 48 | 49 | # Return the state update. 50 | return StateUpdate(delta=delta, events=[]) 51 | -------------------------------------------------------------------------------- /reflex/middleware/middleware.py: -------------------------------------------------------------------------------- 1 | """Base Reflex middleware.""" 2 | 3 | from __future__ import annotations 4 | 5 | from abc import ABC, abstractmethod 6 | from typing import TYPE_CHECKING 7 | 8 | from reflex.event import Event 9 | from reflex.state import BaseState, StateUpdate 10 | 11 | if TYPE_CHECKING: 12 | from reflex.app import App 13 | 14 | 15 | class Middleware(ABC): 16 | """Middleware to preprocess and postprocess requests.""" 17 | 18 | @abstractmethod 19 | async def preprocess( 20 | self, app: App, state: BaseState, event: Event 21 | ) -> StateUpdate | None: 22 | """Preprocess the event. 23 | 24 | Args: 25 | app: The app. 26 | state: The client state. 27 | event: The event to preprocess. 28 | 29 | Returns: 30 | An optional state update to return. 31 | """ 32 | return None 33 | 34 | async def postprocess( 35 | self, app: App, state: BaseState, event: Event, update: StateUpdate 36 | ) -> StateUpdate: 37 | """Postprocess the event. 38 | 39 | Args: 40 | app: The app. 41 | state: The client state. 42 | event: The event to postprocess. 43 | update: The current state update. 44 | 45 | Returns: 46 | An optional state to return. 47 | """ 48 | return update 49 | -------------------------------------------------------------------------------- /reflex/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """Reflex Plugin System.""" 2 | 3 | from .base import CommonContext as CommonContext 4 | from .base import Plugin as Plugin 5 | from .base import PreCompileContext as PreCompileContext 6 | from .tailwind_v3 import Plugin as TailwindV3Plugin 7 | from .tailwind_v4 import Plugin as TailwindV4Plugin 8 | -------------------------------------------------------------------------------- /reflex/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/reflex/py.typed -------------------------------------------------------------------------------- /reflex/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Reflex utilities.""" 2 | -------------------------------------------------------------------------------- /reflex/utils/decorator.py: -------------------------------------------------------------------------------- 1 | """Decorator utilities.""" 2 | 3 | import functools 4 | from collections.abc import Callable 5 | from typing import ParamSpec, TypeVar 6 | 7 | T = TypeVar("T") 8 | 9 | 10 | def once(f: Callable[[], T]) -> Callable[[], T]: 11 | """A decorator that calls the function once and caches the result. 12 | 13 | Args: 14 | f: The function to call. 15 | 16 | Returns: 17 | A function that calls the function once and caches the result. 18 | """ 19 | unset = object() 20 | value: object | T = unset 21 | 22 | @functools.wraps(f) 23 | def wrapper() -> T: 24 | nonlocal value 25 | value = f() if value is unset else value 26 | return value # pyright: ignore[reportReturnType] 27 | 28 | return wrapper 29 | 30 | 31 | def once_unless_none(f: Callable[[], T | None]) -> Callable[[], T | None]: 32 | """A decorator that calls the function once and caches the result unless it is None. 33 | 34 | Args: 35 | f: The function to call. 36 | 37 | Returns: 38 | A function that calls the function once and caches the result unless it is None. 39 | """ 40 | value: T | None = None 41 | 42 | @functools.wraps(f) 43 | def wrapper() -> T | None: 44 | nonlocal value 45 | value = f() if value is None else value 46 | return value 47 | 48 | return wrapper 49 | 50 | 51 | P = ParamSpec("P") 52 | 53 | 54 | def debug(f: Callable[P, T]) -> Callable[P, T]: 55 | """A decorator that prints the function name, arguments, and result. 56 | 57 | Args: 58 | f: The function to call. 59 | 60 | Returns: 61 | A function that prints the function name, arguments, and result. 62 | """ 63 | 64 | @functools.wraps(f) 65 | def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: 66 | result = f(*args, **kwargs) 67 | print( # noqa: T201 68 | f"Calling {f.__name__} with args: {args} and kwargs: {kwargs}, result: {result}" 69 | ) 70 | return result 71 | 72 | return wrapper 73 | -------------------------------------------------------------------------------- /reflex/utils/misc.py: -------------------------------------------------------------------------------- 1 | """Miscellaneous functions for the experimental package.""" 2 | 3 | import asyncio 4 | from collections.abc import Callable 5 | from typing import Any 6 | 7 | 8 | async def run_in_thread(func: Callable) -> Any: 9 | """Run a function in a separate thread. 10 | 11 | To not block the UI event queue, run_in_thread must be inside inside a rx.event(background=True) decorated method. 12 | 13 | Args: 14 | func: The non-async function to run. 15 | 16 | Raises: 17 | ValueError: If the function is an async function. 18 | 19 | Returns: 20 | Any: The return value of the function. 21 | """ 22 | if asyncio.coroutines.iscoroutinefunction(func): 23 | msg = "func must be a non-async function" 24 | raise ValueError(msg) 25 | return await asyncio.get_event_loop().run_in_executor(None, func) 26 | -------------------------------------------------------------------------------- /reflex/utils/redir.py: -------------------------------------------------------------------------------- 1 | """Utilities to handle redirection to browser UI.""" 2 | 3 | import time 4 | import webbrowser 5 | 6 | import httpx 7 | 8 | from reflex import constants 9 | from reflex.utils import net 10 | 11 | from . import console 12 | 13 | 14 | def open_browser(target_url: str) -> None: 15 | """Open a browser window to target_url. 16 | 17 | Args: 18 | target_url: The URL to open in the browser. 19 | """ 20 | if not webbrowser.open(target_url): 21 | console.warn( 22 | f"Unable to automatically open the browser. Please navigate to {target_url} in your browser." 23 | ) 24 | else: 25 | console.info(f"Opening browser to {target_url}.") 26 | 27 | 28 | def open_browser_and_wait( 29 | target_url: str, poll_url: str, interval: int = 2 30 | ) -> httpx.Response: 31 | """Open a browser window to target_url and request poll_url until it returns successfully. 32 | 33 | Args: 34 | target_url: The URL to open in the browser. 35 | poll_url: The URL to poll for success. 36 | interval: The interval in seconds to wait between polling. 37 | 38 | Returns: 39 | The response from the poll_url. 40 | """ 41 | open_browser(target_url) 42 | console.info("[b]Complete the workflow in the browser to continue.[/b]") 43 | while True: 44 | try: 45 | response = net.get(poll_url, follow_redirects=True) 46 | if response.is_success: 47 | break 48 | except httpx.RequestError as err: 49 | console.info(f"Will retry after error occurred while polling: {err}.") 50 | time.sleep(interval) 51 | return response 52 | 53 | 54 | def reflex_build_redirect() -> None: 55 | """Open the browser window to reflex.build.""" 56 | open_browser(constants.Templates.REFLEX_BUILD_FRONTEND) 57 | 58 | 59 | def reflex_templates(): 60 | """Open the browser window to reflex.build/templates.""" 61 | open_browser(constants.Templates.REFLEX_TEMPLATES_URL) 62 | -------------------------------------------------------------------------------- /reflex/utils/registry.py: -------------------------------------------------------------------------------- 1 | """Utilities for working with registries.""" 2 | 3 | import httpx 4 | 5 | from reflex.environment import environment 6 | from reflex.utils import console, net 7 | from reflex.utils.decorator import once 8 | 9 | 10 | def latency(registry: str) -> int: 11 | """Get the latency of a registry. 12 | 13 | Args: 14 | registry (str): The URL of the registry. 15 | 16 | Returns: 17 | int: The latency of the registry in microseconds. 18 | """ 19 | try: 20 | time_to_respond = net.get(registry, timeout=2).elapsed.microseconds 21 | except httpx.HTTPError: 22 | console.info(f"Failed to connect to {registry}.") 23 | return 10_000_000 24 | else: 25 | console.debug(f"Latency of {registry}: {time_to_respond}") 26 | return time_to_respond 27 | 28 | 29 | def average_latency(registry: str, attempts: int = 3) -> int: 30 | """Get the average latency of a registry. 31 | 32 | Args: 33 | registry: The URL of the registry. 34 | attempts: The number of attempts to make. Defaults to 10. 35 | 36 | Returns: 37 | The average latency of the registry in microseconds. 38 | """ 39 | registry_latency = sum(latency(registry) for _ in range(attempts)) // attempts 40 | console.debug(f"Average latency of {registry}: {registry_latency}") 41 | return registry_latency 42 | 43 | 44 | def _get_best_registry() -> str: 45 | """Get the best registry based on latency. 46 | 47 | Returns: 48 | The best registry. 49 | """ 50 | console.debug("Getting best registry...") 51 | registries = [ 52 | ("https://registry.npmjs.org", 1), 53 | ("https://registry.npmmirror.com", 2), 54 | ] 55 | 56 | best_registry = min(registries, key=lambda x: average_latency(x[0]) * x[1])[0] 57 | console.debug(f"Best registry: {best_registry}") 58 | return best_registry 59 | 60 | 61 | @once 62 | def get_npm_registry() -> str: 63 | """Get npm registry. If environment variable is set, use it first. 64 | 65 | Returns: 66 | The npm registry. 67 | """ 68 | return environment.NPM_CONFIG_REGISTRY.get() or _get_best_registry() 69 | -------------------------------------------------------------------------------- /reflex/vars/__init__.py: -------------------------------------------------------------------------------- 1 | """Immutable-Based Var System.""" 2 | 3 | from .base import Field as Field 4 | from .base import LiteralVar as LiteralVar 5 | from .base import Var as Var 6 | from .base import VarData as VarData 7 | from .base import field as field 8 | from .base import get_unique_variable_name as get_unique_variable_name 9 | from .base import get_uuid_string_var as get_uuid_string_var 10 | from .base import var_operation as var_operation 11 | from .base import var_operation_return as var_operation_return 12 | from .datetime import DateTimeVar as DateTimeVar 13 | from .function import FunctionStringVar as FunctionStringVar 14 | from .function import FunctionVar as FunctionVar 15 | from .function import VarOperationCall as VarOperationCall 16 | from .number import BooleanVar as BooleanVar 17 | from .number import LiteralBooleanVar as LiteralBooleanVar 18 | from .number import LiteralNumberVar as LiteralNumberVar 19 | from .number import NumberVar as NumberVar 20 | from .object import LiteralObjectVar as LiteralObjectVar 21 | from .object import ObjectVar as ObjectVar 22 | from .sequence import ArrayVar as ArrayVar 23 | from .sequence import ConcatVarOperation as ConcatVarOperation 24 | from .sequence import LiteralArrayVar as LiteralArrayVar 25 | from .sequence import LiteralStringVar as LiteralStringVar 26 | from .sequence import StringVar as StringVar 27 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | """Utility scripts for the project.""" 2 | -------------------------------------------------------------------------------- /scripts/darglint_test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd .. 3 | 4 | echo "start darglint" 5 | 6 | echo "reflex folder" 7 | for /R reflex %%f in (*.py) do ( 8 | echo %%f 9 | echo %%f|findstr /r "^.*reflex\\rx\.py$" 10 | if errorlevel 1 ( 11 | uv run darglint %%f 12 | ) 13 | ) 14 | 15 | echo "tests folder" 16 | for /R tests %%f in (*.py) do ( 17 | echo %%f 18 | uv run darglint %%f 19 | ) 20 | 21 | echo "darglint finished" 22 | pause 23 | -------------------------------------------------------------------------------- /scripts/hatch_build.py: -------------------------------------------------------------------------------- 1 | """Custom build hook for Hatch.""" 2 | 3 | import importlib.util 4 | import pathlib 5 | import subprocess 6 | import sys 7 | from typing import Any 8 | 9 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 10 | 11 | 12 | class CustomBuilder(BuildHookInterface): 13 | """Custom build hook for Hatch.""" 14 | 15 | PLUGIN_NAME = "custom" 16 | 17 | def marker(self) -> pathlib.Path: 18 | """Get the marker file path. 19 | 20 | Returns: 21 | The marker file path. 22 | """ 23 | return ( 24 | pathlib.Path(self.directory) 25 | / f".reflex-{self.metadata.version}.pyi_generated" 26 | ) 27 | 28 | def finalize( 29 | self, version: str, build_data: dict[str, Any], artifact_path: str 30 | ) -> None: 31 | """Finalize the build process. 32 | 33 | Args: 34 | version: The version of the package. 35 | build_data: The build data. 36 | artifact_path: The path to the artifact. 37 | """ 38 | if self.marker().exists(): 39 | return 40 | 41 | if importlib.util.find_spec("pre_commit") and importlib.util.find_spec("toml"): 42 | import json 43 | 44 | import toml 45 | import yaml 46 | 47 | reflex_dir = pathlib.Path(__file__).parent.parent 48 | pre_commit_config = json.loads( 49 | json.dumps( 50 | toml.load(reflex_dir / "pyproject.toml")["tool"]["pre-commit"] 51 | ) 52 | ) 53 | (reflex_dir / ".pre-commit-config.yaml").write_text( 54 | yaml.dump(pre_commit_config), encoding="utf-8" 55 | ) 56 | 57 | if not (pathlib.Path(self.root) / "scripts").exists(): 58 | return 59 | 60 | for file in (pathlib.Path(self.root) / "reflex").rglob("**/*.pyi"): 61 | file.unlink(missing_ok=True) 62 | 63 | subprocess.run( 64 | [sys.executable, "-m", "reflex.utils.pyi_generator"], 65 | check=True, 66 | ) 67 | self.marker().touch() 68 | -------------------------------------------------------------------------------- /scripts/integration.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Change directory to the first argument passed to the script 4 | project_dir=$1 5 | shift 6 | pushd "$project_dir" || exit 1 7 | echo "Changed directory to $project_dir" 8 | 9 | # So we get stdout / stderr from Python ASAP. Without this, delays can be very long (e.g. on Windows, Github Actions) 10 | export PYTHONUNBUFFERED=1 11 | 12 | env_mode=$1 13 | shift 14 | check_ports=${1:-3000 8000} 15 | shift 16 | 17 | # Start the server in the background 18 | export TELEMETRY_ENABLED=false 19 | reflex run --loglevel debug --env "$env_mode" "$@" & pid=$! 20 | 21 | # Within the context of this bash, $pid_in_bash is what we need to pass to "kill" on exit 22 | # This is true on all platforms. 23 | pid_in_bash=$pid 24 | trap "kill -INT $pid_in_bash ||:" EXIT 25 | 26 | echo "Started server with PID $pid" 27 | 28 | # Assume we run from the root of the repo 29 | popd 30 | 31 | # In Windows, our Python script below needs to work with the WINPID 32 | if [ -f /proc/$pid/winpid ]; then 33 | pid=$(cat /proc/$pid/winpid) 34 | echo "Windows detected, passing winpid $pid to port waiter" 35 | fi 36 | 37 | python scripts/wait_for_listening_port.py $check_ports --timeout=900 --server-pid "$pid" 38 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Root directory for tests.""" 2 | 3 | import os 4 | 5 | from reflex import constants 6 | -------------------------------------------------------------------------------- /tests/benchmarks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/benchmarks/__init__.py -------------------------------------------------------------------------------- /tests/benchmarks/conftest.py: -------------------------------------------------------------------------------- 1 | from .fixtures import evaluated_page, unevaluated_page 2 | 3 | __all__ = ["evaluated_page", "unevaluated_page"] 4 | -------------------------------------------------------------------------------- /tests/benchmarks/test_compilation.py: -------------------------------------------------------------------------------- 1 | from pytest_codspeed import BenchmarkFixture 2 | 3 | from reflex.compiler.compiler import _compile_page, _compile_stateful_components 4 | from reflex.components.component import Component 5 | 6 | 7 | def import_templates(): 8 | # Importing the templates module to avoid the import time in the benchmark 9 | import reflex.compiler.templates # noqa: F401 10 | 11 | 12 | def test_compile_page(evaluated_page: Component, benchmark: BenchmarkFixture): 13 | import_templates() 14 | 15 | benchmark(lambda: _compile_page(evaluated_page, None)) 16 | 17 | 18 | def test_compile_stateful(evaluated_page: Component, benchmark: BenchmarkFixture): 19 | import_templates() 20 | 21 | benchmark(lambda: _compile_stateful_components([evaluated_page])) 22 | 23 | 24 | def test_get_all_imports(evaluated_page: Component, benchmark: BenchmarkFixture): 25 | benchmark(lambda: evaluated_page._get_all_imports()) 26 | -------------------------------------------------------------------------------- /tests/benchmarks/test_evaluate.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | 3 | from pytest_codspeed import BenchmarkFixture 4 | 5 | from reflex.components.component import Component 6 | 7 | 8 | def test_evaluate_page( 9 | unevaluated_page: Callable[[], Component], benchmark: BenchmarkFixture 10 | ): 11 | benchmark(unevaluated_page) 12 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- 1 | """Package for integration tests.""" 2 | -------------------------------------------------------------------------------- /tests/integration/conftest.py: -------------------------------------------------------------------------------- 1 | """Shared conftest for all integration tests.""" 2 | 3 | import pytest 4 | from pytest_mock import MockerFixture 5 | 6 | import reflex.app 7 | from reflex.testing import AppHarness, AppHarnessProd 8 | 9 | 10 | @pytest.fixture( 11 | scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"] 12 | ) 13 | def app_harness_env(request): 14 | """Parametrize the AppHarness class to use for the test, either dev or prod. 15 | 16 | Args: 17 | request: The pytest fixture request object. 18 | 19 | Returns: 20 | The AppHarness class to use for the test. 21 | """ 22 | return request.param 23 | 24 | 25 | @pytest.fixture(autouse=True) 26 | def raise_console_error(request, mocker: MockerFixture): 27 | """Spy on calls to `console.error` used by the framework. 28 | 29 | Help catch spurious error conditions that might otherwise go unnoticed. 30 | 31 | If a test is marked with `ignore_console_error`, the spy will be ignored 32 | after the test. 33 | 34 | Args: 35 | request: The pytest request object. 36 | mocker: The pytest mocker object. 37 | 38 | Yields: 39 | control to the test function. 40 | """ 41 | spy = mocker.spy(reflex.app.console, "error") 42 | yield 43 | if "ignore_console_error" not in request.keywords: 44 | spy.assert_not_called() 45 | -------------------------------------------------------------------------------- /tests/integration/init-test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | ARG USERNAME=kerrigan 4 | RUN useradd -m $USERNAME 5 | RUN apt-get update && apt-get install -y redis && rm -rf /var/lib/apt/lists/* 6 | USER $USERNAME 7 | 8 | WORKDIR /home/$USERNAME 9 | -------------------------------------------------------------------------------- /tests/integration/init-test/in_docker_test_script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 6 | export TELEMETRY_ENABLED=false 7 | 8 | function do_export () { 9 | template=$1 10 | mkdir ~/"$template" 11 | cd ~/"$template" 12 | rm -rf ~/.local/share/reflex ~/"$template"/.web 13 | reflex init --template "$template" 14 | reflex export 15 | ( 16 | cd "$SCRIPTPATH/../../.." 17 | scripts/integration.sh ~/"$template" dev 18 | pkill -9 -f 'next-server|python3' || true 19 | sleep 10 20 | REDIS_URL=redis://localhost scripts/integration.sh ~/"$template" prod 21 | pkill -9 -f 'next-server|python3' || true 22 | sleep 10 23 | ) 24 | } 25 | 26 | echo "Preparing test project dir" 27 | python3 -m venv ~/venv 28 | source ~/venv/bin/activate 29 | pip install -U pip 30 | 31 | echo "Installing reflex from local repo code" 32 | cp -r /reflex-repo ~/reflex-repo 33 | pip install ~/reflex-repo 34 | 35 | redis-server & 36 | 37 | echo "Running reflex init in test project dir" 38 | do_export blank 39 | -------------------------------------------------------------------------------- /tests/integration/shared/state.py: -------------------------------------------------------------------------------- 1 | """Simple module which contains one reusable reflex state class.""" 2 | 3 | import reflex as rx 4 | 5 | 6 | class SharedState(rx.State): 7 | """Shared state class for reflexers using librarys.""" 8 | -------------------------------------------------------------------------------- /tests/integration/test_shared_state.py: -------------------------------------------------------------------------------- 1 | """Test shared state.""" 2 | 3 | from __future__ import annotations 4 | 5 | from collections.abc import Generator 6 | 7 | import pytest 8 | 9 | from reflex.testing import AppHarness, WebDriver 10 | 11 | 12 | def SharedStateApp(): 13 | """Test that shared state works as expected.""" 14 | import reflex as rx 15 | from tests.integration.shared.state import SharedState 16 | 17 | class State(SharedState): 18 | pass 19 | 20 | def index() -> rx.Component: 21 | return rx.vstack() 22 | 23 | app = rx.App() 24 | app.add_page(index) 25 | 26 | 27 | @pytest.fixture 28 | def shared_state( 29 | tmp_path_factory, 30 | ) -> Generator[AppHarness, None, None]: 31 | """Start SharedStateApp at tmp_path via AppHarness. 32 | 33 | Args: 34 | tmp_path_factory: pytest tmp_path_factory fixture 35 | 36 | Yields: 37 | running AppHarness instance 38 | 39 | """ 40 | with AppHarness.create( 41 | root=tmp_path_factory.mktemp("shared_state"), 42 | app_source=SharedStateApp, 43 | ) as harness: 44 | yield harness 45 | 46 | 47 | @pytest.fixture 48 | def driver(shared_state: AppHarness) -> Generator[WebDriver, None, None]: 49 | """Get an instance of the browser open to the shared_state app. 50 | 51 | Args: 52 | shared_state: harness for SharedStateApp 53 | 54 | Yields: 55 | WebDriver instance. 56 | 57 | """ 58 | assert shared_state.app_instance is not None, "app is not running" 59 | driver = shared_state.frontend() 60 | try: 61 | yield driver 62 | finally: 63 | driver.quit() 64 | 65 | 66 | def test_shared_state( 67 | shared_state: AppHarness, 68 | driver: WebDriver, 69 | ): 70 | """Test that 2 AppHarness instances can share a state (f.e. from a library). 71 | 72 | Args: 73 | shared_state: harness for SharedStateApp. 74 | driver: WebDriver instance. 75 | 76 | """ 77 | assert shared_state.app_instance is not None 78 | -------------------------------------------------------------------------------- /tests/integration/tests_playwright/test_link_hover.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Generator 2 | 3 | import pytest 4 | from playwright.sync_api import Page, expect 5 | 6 | from reflex.testing import AppHarness 7 | 8 | 9 | def LinkApp(): 10 | import reflex as rx 11 | 12 | app = rx.App() 13 | 14 | def index(): 15 | return rx.vstack( 16 | rx.box(height="10em"), # spacer, so the link isn't hovered initially 17 | rx.link( 18 | "Click me", 19 | href="#", 20 | color="blue", 21 | _hover=rx.Style({"color": "red"}), 22 | ), 23 | ) 24 | 25 | app.add_page(index, "/") 26 | 27 | 28 | @pytest.fixture 29 | def link_app(tmp_path_factory) -> Generator[AppHarness, None, None]: 30 | with AppHarness.create( 31 | root=tmp_path_factory.mktemp("link_app"), 32 | app_source=LinkApp, 33 | ) as harness: 34 | assert harness.app_instance is not None, "app is not running" 35 | yield harness 36 | 37 | 38 | def test_link_hover(link_app: AppHarness, page: Page): 39 | assert link_app.frontend_url is not None 40 | page.goto(link_app.frontend_url) 41 | 42 | link = page.get_by_role("link") 43 | expect(link).to_have_text("Click me") 44 | expect(link).to_have_css("color", "rgb(0, 0, 255)") 45 | link.hover() 46 | expect(link).to_have_css("color", "rgb(255, 0, 0)") 47 | -------------------------------------------------------------------------------- /tests/integration/tests_playwright/test_stateless_app.py: -------------------------------------------------------------------------------- 1 | """Integration tests for a stateless app.""" 2 | 3 | from collections.abc import Generator 4 | 5 | import httpx 6 | import pytest 7 | from playwright.sync_api import Page, expect 8 | 9 | import reflex as rx 10 | from reflex.testing import AppHarness 11 | 12 | 13 | def StatelessApp(): 14 | """A stateless app that renders a heading.""" 15 | import reflex as rx 16 | 17 | def index(): 18 | return rx.heading("This is a stateless app") 19 | 20 | app = rx.App() 21 | app.add_page(index) 22 | 23 | 24 | @pytest.fixture(scope="module") 25 | def stateless_app(tmp_path_factory) -> Generator[AppHarness, None, None]: 26 | """Create a stateless app AppHarness. 27 | 28 | Args: 29 | tmp_path_factory: pytest fixture for creating temporary directories. 30 | 31 | Yields: 32 | AppHarness: A harness for testing the stateless app. 33 | """ 34 | with AppHarness.create( 35 | root=tmp_path_factory.mktemp("stateless_app"), 36 | app_source=StatelessApp, 37 | ) as harness: 38 | yield harness 39 | 40 | 41 | def test_statelessness(stateless_app: AppHarness, page: Page): 42 | """Test that the stateless app renders a heading but backend/_event is not mounted. 43 | 44 | Args: 45 | stateless_app: A harness for testing the stateless app. 46 | page: A Playwright page. 47 | """ 48 | assert stateless_app.frontend_url is not None 49 | assert stateless_app.backend is not None 50 | assert stateless_app.backend.started 51 | 52 | res = httpx.get(rx.config.get_config().api_url + "/_event") 53 | assert res.status_code == 404 54 | 55 | res2 = httpx.get(rx.config.get_config().api_url + "/ping") 56 | assert res2.status_code == 200 57 | 58 | page.goto(stateless_app.frontend_url) 59 | expect(page.get_by_role("heading")).to_have_text("This is a stateless app") 60 | -------------------------------------------------------------------------------- /tests/units/__init__.py: -------------------------------------------------------------------------------- 1 | """Root directory for tests.""" 2 | 3 | import os 4 | 5 | from reflex import constants 6 | -------------------------------------------------------------------------------- /tests/units/assets/custom_script.js: -------------------------------------------------------------------------------- 1 | const test = "inside custom_script.js"; 2 | -------------------------------------------------------------------------------- /tests/units/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | """Compiler tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/compiler/test_compiler_utils.py: -------------------------------------------------------------------------------- 1 | def TestState(State): 2 | pass 3 | 4 | 5 | def test_compile_state(): 6 | # TODO: Implement test for compile_state function. 7 | pass 8 | -------------------------------------------------------------------------------- /tests/units/components/__init__.py: -------------------------------------------------------------------------------- 1 | """Component tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/components/base/test_bare.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.components.base.bare import Bare 4 | from reflex.vars.base import Var 5 | 6 | STATE_VAR = Var(_js_expr="default_state.name") 7 | 8 | 9 | @pytest.mark.parametrize( 10 | ("contents", "expected"), 11 | [ 12 | ("hello", '"hello"'), 13 | ("{}", '"{}"'), 14 | (None, '""'), 15 | (STATE_VAR, "default_state.name"), 16 | ], 17 | ) 18 | def test_fstrings(contents, expected): 19 | """Test that fstrings are rendered correctly. 20 | 21 | Args: 22 | contents: The contents of the component. 23 | expected: The expected output. 24 | """ 25 | comp = Bare.create(contents).render() 26 | assert comp["contents"] == expected 27 | -------------------------------------------------------------------------------- /tests/units/components/base/test_link.py: -------------------------------------------------------------------------------- 1 | from reflex.components.base.link import RawLink, ScriptTag 2 | 3 | 4 | def test_raw_link(): 5 | raw_link = RawLink.create("https://example.com").render() 6 | assert raw_link["name"] == '"link"' 7 | assert raw_link["children"][0]["contents"] == '"https://example.com"' 8 | 9 | 10 | def test_script_tag(): 11 | script_tag = ScriptTag.create("console.log('Hello, world!');").render() 12 | assert script_tag["name"] == '"script"' 13 | assert script_tag["children"][0]["contents"] == "\"console.log('Hello, world!');\"" 14 | -------------------------------------------------------------------------------- /tests/units/components/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/core/__init__.py -------------------------------------------------------------------------------- /tests/units/components/core/test_banner.py: -------------------------------------------------------------------------------- 1 | from reflex.components.core.banner import ( 2 | ConnectionBanner, 3 | ConnectionModal, 4 | ConnectionPulser, 5 | WebsocketTargetURL, 6 | ) 7 | from reflex.components.radix.themes.base import RadixThemesComponent 8 | from reflex.components.radix.themes.typography.text import Text 9 | 10 | 11 | def test_websocket_target_url(): 12 | url = WebsocketTargetURL.create() 13 | var_data = url._get_all_var_data() 14 | assert var_data is not None 15 | assert sorted(key for key, _ in var_data.imports) == sorted( 16 | ("$/utils/state", "$/env.json") 17 | ) 18 | 19 | 20 | def test_connection_banner(): 21 | banner = ConnectionBanner.create() 22 | _imports = banner._get_all_imports(collapse=True) 23 | assert sorted(_imports) == sorted( 24 | ( 25 | "react", 26 | "$/utils/context", 27 | "$/utils/state", 28 | RadixThemesComponent.create().library or "", 29 | "$/env.json", 30 | ) 31 | ) 32 | 33 | msg = "Connection error" 34 | custom_banner = ConnectionBanner.create(Text.create(msg)) 35 | assert msg in str(custom_banner.render()) 36 | 37 | 38 | def test_connection_modal(): 39 | modal = ConnectionModal.create() 40 | _imports = modal._get_all_imports(collapse=True) 41 | assert sorted(_imports) == sorted( 42 | ( 43 | "react", 44 | "$/utils/context", 45 | "$/utils/state", 46 | RadixThemesComponent.create().library or "", 47 | "$/env.json", 48 | ) 49 | ) 50 | 51 | msg = "Connection error" 52 | custom_modal = ConnectionModal.create(Text.create(msg)) 53 | assert msg in str(custom_modal.render()) 54 | 55 | 56 | def test_connection_pulser(): 57 | pulser = ConnectionPulser.create() 58 | _custom_code = pulser._get_all_custom_code() 59 | _imports = pulser._get_all_imports(collapse=True) 60 | -------------------------------------------------------------------------------- /tests/units/components/core/test_html.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.components.core.html import Html 4 | from reflex.state import State 5 | 6 | 7 | def test_html_no_children(): 8 | with pytest.raises(ValueError): 9 | _ = Html.create() 10 | 11 | 12 | def test_html_many_children(): 13 | with pytest.raises(ValueError): 14 | _ = Html.create("foo", "bar") 15 | 16 | 17 | def test_html_create(): 18 | html = Html.create("

Hello !

") 19 | assert str(html.dangerouslySetInnerHTML) == '({ ["__html"] : "

Hello !

" })' # pyright: ignore [reportAttributeAccessIssue] 20 | assert ( 21 | str(html) 22 | == 'jsx("div",{className:"rx-Html",dangerouslySetInnerHTML:({ ["__html"] : "

Hello !

" })},)\n' 23 | ) 24 | 25 | 26 | def test_html_fstring_create(): 27 | class TestState(State): 28 | """The app state.""" 29 | 30 | myvar: str = "Blue" 31 | 32 | html = Html.create(f"

Hello {TestState.myvar}!

") 33 | 34 | html_dangerouslySetInnerHTML = html.dangerouslySetInnerHTML # pyright: ignore [reportAttributeAccessIssue] 35 | 36 | assert ( 37 | str(html_dangerouslySetInnerHTML) 38 | == f'({{ ["__html"] : ("

Hello "+{TestState.myvar!s}+"!

") }})' 39 | ) 40 | assert ( 41 | str(html) 42 | == f'jsx("div",{{className:"rx-Html",dangerouslySetInnerHTML:{html_dangerouslySetInnerHTML!s}}},)\n' 43 | ) 44 | -------------------------------------------------------------------------------- /tests/units/components/core/test_responsive.py: -------------------------------------------------------------------------------- 1 | from reflex.components.core.responsive import ( 2 | desktop_only, 3 | mobile_and_tablet, 4 | mobile_only, 5 | tablet_and_desktop, 6 | tablet_only, 7 | ) 8 | from reflex.components.radix.themes.layout.box import Box 9 | 10 | 11 | def test_mobile_only(): 12 | """Test the mobile_only responsive component.""" 13 | component = mobile_only("Content") 14 | assert isinstance(component, Box) 15 | 16 | 17 | def test_tablet_only(): 18 | """Test the tablet_only responsive component.""" 19 | component = tablet_only("Content") 20 | assert isinstance(component, Box) 21 | 22 | 23 | def test_desktop_only(): 24 | """Test the desktop_only responsive component.""" 25 | component = desktop_only("Content") 26 | assert isinstance(component, Box) 27 | 28 | 29 | def test_tablet_and_desktop(): 30 | """Test the tablet_and_desktop responsive component.""" 31 | component = tablet_and_desktop("Content") 32 | assert isinstance(component, Box) 33 | 34 | 35 | def test_mobile_and_tablet(): 36 | """Test the mobile_and_tablet responsive component.""" 37 | component = mobile_and_tablet("Content") 38 | assert isinstance(component, Box) 39 | -------------------------------------------------------------------------------- /tests/units/components/datadisplay/__init__.py: -------------------------------------------------------------------------------- 1 | """Datadisplay component tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/components/datadisplay/conftest.py: -------------------------------------------------------------------------------- 1 | """Data display component tests fixtures.""" 2 | 3 | import pandas as pd 4 | import pytest 5 | 6 | import reflex as rx 7 | from reflex.state import BaseState 8 | 9 | 10 | @pytest.fixture 11 | def data_table_state(request): 12 | """Get a data table state. 13 | 14 | Args: 15 | request: The request. 16 | 17 | Returns: 18 | The data table state class. 19 | """ 20 | 21 | class DataTableState(BaseState): 22 | data = request.param["data"] 23 | columns = ["column1", "column2"] 24 | 25 | return DataTableState 26 | 27 | 28 | @pytest.fixture 29 | def data_table_state2(): 30 | """Get a data table state. 31 | 32 | Returns: 33 | The data table state class. 34 | """ 35 | 36 | class DataTableState(BaseState): 37 | _data = pd.DataFrame() 38 | 39 | @rx.var 40 | def data(self): 41 | return self._data 42 | 43 | return DataTableState 44 | 45 | 46 | @pytest.fixture 47 | def data_table_state3(): 48 | """Get a data table state. 49 | 50 | Returns: 51 | The data table state class. 52 | """ 53 | 54 | class DataTableState(BaseState): 55 | _data: list = [] 56 | _columns: list = ["col1", "col2"] 57 | 58 | @rx.var 59 | def data(self) -> list: 60 | return self._data 61 | 62 | @rx.var 63 | def columns(self): 64 | return self._columns 65 | 66 | return DataTableState 67 | 68 | 69 | @pytest.fixture 70 | def data_table_state4(): 71 | """Get a data table state. 72 | 73 | Returns: 74 | The data table state class. 75 | """ 76 | 77 | class DataTableState(BaseState): 78 | _data: list = [] 79 | _columns: list[str] = ["col1", "col2"] 80 | 81 | @rx.var 82 | def data(self): 83 | return self._data 84 | 85 | @rx.var 86 | def columns(self) -> list: 87 | return self._columns 88 | 89 | return DataTableState 90 | -------------------------------------------------------------------------------- /tests/units/components/datadisplay/test_code.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.components.datadisplay.code import CodeBlock, Theme 4 | 5 | 6 | @pytest.mark.parametrize( 7 | ("theme", "expected"), 8 | [(Theme.one_light, "oneLight"), (Theme.one_dark, "oneDark")], 9 | ) 10 | def test_code_light_dark_theme(theme, expected): 11 | code_block = CodeBlock.create(theme=theme) 12 | 13 | assert code_block.theme._js_expr == expected # pyright: ignore [reportAttributeAccessIssue] 14 | -------------------------------------------------------------------------------- /tests/units/components/datadisplay/test_dataeditor.py: -------------------------------------------------------------------------------- 1 | from reflex.components.datadisplay.dataeditor import DataEditor 2 | 3 | 4 | def test_dataeditor(): 5 | editor_wrapper = DataEditor.create().render() 6 | editor = editor_wrapper["children"][0] 7 | assert editor_wrapper["name"] == '"div"' 8 | assert editor_wrapper["props"] == [ 9 | 'css:({ ["width"] : "100%", ["height"] : "100%" })' 10 | ] 11 | assert editor["name"] == "DataEditor" 12 | -------------------------------------------------------------------------------- /tests/units/components/el/test_html.py: -------------------------------------------------------------------------------- 1 | from reflex.components.el.constants.html import ATTR_TO_ELEMENTS, ELEMENTS 2 | 3 | 4 | def test_html_constants(): 5 | assert ELEMENTS 6 | assert ATTR_TO_ELEMENTS 7 | -------------------------------------------------------------------------------- /tests/units/components/el/test_svg.py: -------------------------------------------------------------------------------- 1 | from reflex.components.el.elements.media import ( 2 | Circle, 3 | Defs, 4 | Ellipse, 5 | G, 6 | Line, 7 | LinearGradient, 8 | Path, 9 | Polygon, 10 | RadialGradient, 11 | Rect, 12 | Stop, 13 | Svg, 14 | Text, 15 | ) 16 | 17 | 18 | def test_circle(): 19 | circle = Circle.create().render() 20 | assert circle["name"] == '"circle"' 21 | 22 | 23 | def test_defs(): 24 | defs = Defs.create().render() 25 | assert defs["name"] == '"defs"' 26 | 27 | 28 | def test_ellipse(): 29 | ellipse = Ellipse.create().render() 30 | assert ellipse["name"] == '"ellipse"' 31 | 32 | 33 | def test_line(): 34 | line = Line.create().render() 35 | assert line["name"] == '"line"' 36 | 37 | 38 | def test_linear_gradient(): 39 | linear_gradient = LinearGradient.create().render() 40 | assert linear_gradient["name"] == '"linearGradient"' 41 | 42 | 43 | def test_path(): 44 | path = Path.create().render() 45 | assert path["name"] == '"path"' 46 | 47 | 48 | def test_polygon(): 49 | polygon = Polygon.create().render() 50 | assert polygon["name"] == '"polygon"' 51 | 52 | 53 | def test_radial_gradient(): 54 | radial_gradient = RadialGradient.create().render() 55 | assert radial_gradient["name"] == '"radialGradient"' 56 | 57 | 58 | def test_rect(): 59 | rect = Rect.create().render() 60 | assert rect["name"] == '"rect"' 61 | 62 | 63 | def test_svg(): 64 | svg = Svg.create().render() 65 | assert svg["name"] == '"svg"' 66 | 67 | 68 | def test_text(): 69 | text = Text.create().render() 70 | assert text["name"] == '"text"' 71 | 72 | 73 | def test_stop(): 74 | stop = Stop.create().render() 75 | assert stop["name"] == '"stop"' 76 | 77 | 78 | def test_g(): 79 | g = G.create().render() 80 | assert g["name"] == '"g"' 81 | -------------------------------------------------------------------------------- /tests/units/components/forms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/forms/__init__.py -------------------------------------------------------------------------------- /tests/units/components/forms/test_form.py: -------------------------------------------------------------------------------- 1 | from reflex.components.radix.primitives.form import Form 2 | from reflex.event import EventChain, prevent_default 3 | from reflex.vars.base import Var 4 | 5 | 6 | def test_render_on_submit(): 7 | """Test that on_submit event chain is rendered as a separate function.""" 8 | submit_it = Var( 9 | _js_expr="submit_it", 10 | _var_type=EventChain, 11 | ) 12 | f = Form.create(on_submit=submit_it) 13 | exp_submit_name = f"handleSubmit_{f.handle_submit_unique_name}" # pyright: ignore [reportAttributeAccessIssue] 14 | assert f"onSubmit:{exp_submit_name}" in f.render()["props"] 15 | 16 | 17 | def test_render_no_on_submit(): 18 | """A form without on_submit should render a prevent_default handler.""" 19 | f = Form.create() 20 | assert isinstance(f.event_triggers["on_submit"], EventChain) 21 | assert len(f.event_triggers["on_submit"].events) == 1 22 | assert f.event_triggers["on_submit"].events[0] == prevent_default 23 | -------------------------------------------------------------------------------- /tests/units/components/graphing/__init__.py: -------------------------------------------------------------------------------- 1 | """Graphing component tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/components/graphing/test_plotly.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import plotly.graph_objects as go 3 | import pytest 4 | 5 | import reflex as rx 6 | from reflex.utils.serializers import serialize, serialize_figure 7 | 8 | 9 | @pytest.fixture 10 | def plotly_fig() -> go.Figure: 11 | """Get a plotly figure. 12 | 13 | Returns: 14 | A random plotly figure. 15 | """ 16 | # Generate random data. 17 | rng = np.random.default_rng() 18 | data = rng.integers(0, 10, size=(10, 4)) 19 | trace = go.Scatter( 20 | x=list(range(len(data))), y=data[:, 0], mode="lines", name="Trace 1" 21 | ) 22 | 23 | # Create a graph. 24 | return go.Figure(data=[trace]) 25 | 26 | 27 | def test_serialize_plotly(plotly_fig: go.Figure): 28 | """Test that serializing a plotly figure works. 29 | 30 | Args: 31 | plotly_fig: The figure to serialize. 32 | """ 33 | value = serialize(plotly_fig) 34 | assert isinstance(value, dict) 35 | assert value == serialize_figure(plotly_fig) 36 | 37 | 38 | def test_plotly_config_option(plotly_fig: go.Figure): 39 | """Test that the plotly component can be created with a config option. 40 | 41 | Args: 42 | plotly_fig: The figure to display. 43 | """ 44 | # This tests just confirm that the component can be created with a config option. 45 | _ = rx.plotly(data=plotly_fig, config={"showLink": True}) 46 | -------------------------------------------------------------------------------- /tests/units/components/graphing/test_recharts.py: -------------------------------------------------------------------------------- 1 | from reflex.components.recharts.charts import ( 2 | AreaChart, 3 | BarChart, 4 | LineChart, 5 | PieChart, 6 | RadarChart, 7 | RadialBarChart, 8 | ScatterChart, 9 | ) 10 | from reflex.components.recharts.general import ResponsiveContainer 11 | 12 | 13 | def test_area_chart(): 14 | ac = AreaChart.create() 15 | assert isinstance(ac, ResponsiveContainer) 16 | assert isinstance(ac.children[0], AreaChart) 17 | 18 | 19 | def test_bar_chart(): 20 | bc = BarChart.create() 21 | assert isinstance(bc, ResponsiveContainer) 22 | assert isinstance(bc.children[0], BarChart) 23 | 24 | 25 | def test_line_chart(): 26 | lc = LineChart.create() 27 | assert isinstance(lc, ResponsiveContainer) 28 | assert isinstance(lc.children[0], LineChart) 29 | 30 | 31 | def test_pie_chart(): 32 | pc = PieChart.create() 33 | assert isinstance(pc, ResponsiveContainer) 34 | assert isinstance(pc.children[0], PieChart) 35 | 36 | 37 | def test_radar_chart(): 38 | rc = RadarChart.create() 39 | assert isinstance(rc, ResponsiveContainer) 40 | assert isinstance(rc.children[0], RadarChart) 41 | 42 | 43 | def test_radial_bar_chart(): 44 | rbc = RadialBarChart.create() 45 | assert isinstance(rbc, ResponsiveContainer) 46 | assert isinstance(rbc.children[0], RadialBarChart) 47 | 48 | 49 | def test_scatter_chart(): 50 | sc = ScatterChart.create() 51 | assert isinstance(sc, ResponsiveContainer) 52 | assert isinstance(sc.children[0], ScatterChart) 53 | -------------------------------------------------------------------------------- /tests/units/components/layout/__init__.py: -------------------------------------------------------------------------------- 1 | """Layout component tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/components/lucide/test_icon.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.components.lucide.icon import ( 4 | LUCIDE_ICON_LIST, 5 | LUCIDE_ICON_MAPPING_OVERRIDE, 6 | Icon, 7 | ) 8 | from reflex.utils import format 9 | 10 | 11 | @pytest.mark.parametrize("tag", LUCIDE_ICON_LIST) 12 | def test_icon(tag): 13 | icon = Icon.create(tag) 14 | assert icon.alias == "Lucide" + LUCIDE_ICON_MAPPING_OVERRIDE.get( 15 | tag, format.to_title_case(tag) 16 | ) 17 | 18 | 19 | def test_icon_missing_tag(): 20 | with pytest.raises(AttributeError): 21 | _ = Icon.create() 22 | 23 | 24 | def test_icon_invalid_tag(): 25 | invalid = Icon.create("invalid-tag") 26 | assert invalid.tag == "CircleHelp" 27 | 28 | 29 | def test_icon_multiple_children(): 30 | with pytest.raises(AttributeError): 31 | _ = Icon.create("activity", "child1", "child2") 32 | 33 | 34 | def test_icon_add_style(): 35 | ic = Icon.create("activity") 36 | assert ic.add_style() is None 37 | -------------------------------------------------------------------------------- /tests/units/components/markdown/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/markdown/__init__.py -------------------------------------------------------------------------------- /tests/units/components/media/__init__.py: -------------------------------------------------------------------------------- 1 | """Media component tests.""" 2 | -------------------------------------------------------------------------------- /tests/units/components/radix/test_icon_button.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.components.lucide.icon import Icon 4 | from reflex.components.radix.themes.components.icon_button import IconButton 5 | from reflex.style import Style 6 | from reflex.vars.base import LiteralVar 7 | 8 | 9 | def test_icon_button(): 10 | ib1 = IconButton.create("activity") 11 | assert isinstance(ib1, IconButton) 12 | 13 | ib2 = IconButton.create(Icon.create("activity")) 14 | assert isinstance(ib2, IconButton) 15 | 16 | assert isinstance(ib1.add_style(), Style) 17 | assert isinstance(ib2.add_style(), Style) 18 | 19 | 20 | def test_icon_button_no_child(): 21 | with pytest.raises(ValueError): 22 | _ = IconButton.create() 23 | 24 | 25 | def test_icon_button_size_prop(): 26 | ib1 = IconButton.create("activity", size="2") 27 | assert isinstance(ib1, IconButton) 28 | 29 | ib2 = IconButton.create("activity", size=LiteralVar.create("2")) 30 | assert isinstance(ib2, IconButton) 31 | -------------------------------------------------------------------------------- /tests/units/components/radix/test_layout.py: -------------------------------------------------------------------------------- 1 | from reflex.components.radix.themes.layout.base import LayoutComponent 2 | 3 | 4 | def test_layout_component(): 5 | lc = LayoutComponent.create() 6 | assert isinstance(lc, LayoutComponent) 7 | -------------------------------------------------------------------------------- /tests/units/components/recharts/test_cartesian.py: -------------------------------------------------------------------------------- 1 | from reflex.components.recharts import ( 2 | Area, 3 | Bar, 4 | Brush, 5 | Line, 6 | Scatter, 7 | XAxis, 8 | YAxis, 9 | ZAxis, 10 | ) 11 | 12 | 13 | def test_xaxis(): 14 | x_axis = XAxis.create("x").render() 15 | assert x_axis["name"] == "RechartsXAxis" 16 | 17 | 18 | def test_yaxis(): 19 | x_axis = YAxis.create("y").render() 20 | assert x_axis["name"] == "RechartsYAxis" 21 | 22 | 23 | def test_zaxis(): 24 | x_axis = ZAxis.create("z").render() 25 | assert x_axis["name"] == "RechartsZAxis" 26 | 27 | 28 | def test_brush(): 29 | brush = Brush.create().render() 30 | assert brush["name"] == "RechartsBrush" 31 | 32 | 33 | def test_area(): 34 | area = Area.create().render() 35 | assert area["name"] == "RechartsArea" 36 | 37 | 38 | def test_bar(): 39 | bar = Bar.create().render() 40 | assert bar["name"] == "RechartsBar" 41 | 42 | 43 | def test_line(): 44 | line = Line.create().render() 45 | assert line["name"] == "RechartsLine" 46 | 47 | 48 | def test_scatter(): 49 | scatter = Scatter.create().render() 50 | assert scatter["name"] == "RechartsScatter" 51 | -------------------------------------------------------------------------------- /tests/units/components/recharts/test_polar.py: -------------------------------------------------------------------------------- 1 | from reflex.components.recharts import ( 2 | Pie, 3 | PolarAngleAxis, 4 | PolarGrid, 5 | PolarRadiusAxis, 6 | Radar, 7 | RadialBar, 8 | ) 9 | 10 | 11 | def test_pie(): 12 | pie = Pie.create().render() 13 | assert pie["name"] == "RechartsPie" 14 | 15 | 16 | def test_radar(): 17 | radar = Radar.create().render() 18 | assert radar["name"] == "RechartsRadar" 19 | 20 | 21 | def test_radialbar(): 22 | radialbar = RadialBar.create().render() 23 | assert radialbar["name"] == "RechartsRadialBar" 24 | 25 | 26 | def test_polarangleaxis(): 27 | polarangleaxis = PolarAngleAxis.create().render() 28 | assert polarangleaxis["name"] == "RechartsPolarAngleAxis" 29 | 30 | 31 | def test_polargrid(): 32 | polargrid = PolarGrid.create().render() 33 | assert polargrid["name"] == "RechartsPolarGrid" 34 | 35 | 36 | def test_polarradiusaxis(): 37 | polarradiusaxis = PolarRadiusAxis.create().render() 38 | assert polarradiusaxis["name"] == "RechartsPolarRadiusAxis" 39 | -------------------------------------------------------------------------------- /tests/units/components/test_component_future_annotations.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from typing import Any 4 | 5 | from reflex.components.component import Component 6 | from reflex.event import EventHandler, input_event, no_args_event_spec 7 | 8 | 9 | # This is a repeat of its namesake in test_component.py. 10 | def test_custom_component_declare_event_handlers_in_fields(): 11 | class ReferenceComponent(Component): 12 | def get_event_triggers(self) -> dict[str, Any]: 13 | """Test controlled triggers. 14 | 15 | Returns: 16 | Test controlled triggers. 17 | """ 18 | return { 19 | **super().get_event_triggers(), 20 | "on_a": lambda e: [e], 21 | "on_b": lambda e: [e.target.value], 22 | "on_c": lambda e: [], 23 | "on_d": no_args_event_spec, 24 | } 25 | 26 | class TestComponent(Component): 27 | on_a: EventHandler[lambda e0: [e0]] 28 | on_b: EventHandler[input_event] 29 | on_c: EventHandler[no_args_event_spec] 30 | on_d: EventHandler[no_args_event_spec] 31 | 32 | custom_component = ReferenceComponent.create() 33 | test_component = TestComponent.create() 34 | assert ( 35 | custom_component.get_event_triggers().keys() 36 | == test_component.get_event_triggers().keys() 37 | ) 38 | -------------------------------------------------------------------------------- /tests/units/components/test_component_state.py: -------------------------------------------------------------------------------- 1 | """Ensure that Components returned by ComponentState.create have independent State classes.""" 2 | 3 | import pytest 4 | 5 | import reflex as rx 6 | from reflex.components.base.bare import Bare 7 | from reflex.utils.exceptions import ReflexRuntimeError 8 | 9 | 10 | def test_component_state(): 11 | """Create two components with independent state classes.""" 12 | 13 | class CS(rx.ComponentState): 14 | count: int = 0 15 | 16 | def increment(self): 17 | self.count += 1 18 | 19 | @classmethod 20 | def get_component(cls, *children, **props): 21 | return rx.el.div( 22 | *children, 23 | **props, 24 | ) 25 | 26 | cs1, cs2 = CS.create("a", id="a"), CS.create("b", id="b") 27 | assert isinstance(cs1, rx.Component) 28 | assert isinstance(cs2, rx.Component) 29 | assert cs1.State is not None 30 | assert cs2.State is not None 31 | assert cs1.State != cs2.State 32 | assert issubclass(cs1.State, CS) 33 | assert issubclass(cs1.State, rx.State) 34 | assert issubclass(cs2.State, CS) 35 | assert issubclass(cs2.State, rx.State) 36 | assert CS._per_component_state_instance_count == 2 37 | assert isinstance(cs1.State.increment, rx.event.EventHandler) 38 | assert cs1.State.increment != cs2.State.increment 39 | 40 | assert len(cs1.children) == 1 41 | assert cs1.children[0].render() == Bare.create("a").render() 42 | assert cs1.id == "a" 43 | assert len(cs2.children) == 1 44 | assert cs2.children[0].render() == Bare.create("b").render() 45 | assert cs2.id == "b" 46 | 47 | 48 | def test_init_component_state() -> None: 49 | """Ensure that ComponentState subclasses cannot be instantiated directly.""" 50 | 51 | class CS(rx.ComponentState): 52 | @classmethod 53 | def get_component(cls, *children, **props): 54 | return rx.el.div() 55 | 56 | with pytest.raises(ReflexRuntimeError): 57 | CS() 58 | 59 | class SubCS(CS): 60 | pass 61 | 62 | with pytest.raises(ReflexRuntimeError): 63 | SubCS() 64 | -------------------------------------------------------------------------------- /tests/units/components/typography/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/components/typography/__init__.py -------------------------------------------------------------------------------- /tests/units/components/typography/test_markdown.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import reflex as rx 4 | from reflex.components.markdown import Markdown 5 | 6 | 7 | @pytest.mark.parametrize( 8 | ("tag", "expected"), 9 | [ 10 | ("h1", "Heading"), 11 | ("h2", "Heading"), 12 | ("h3", "Heading"), 13 | ("h4", "Heading"), 14 | ("h5", "Heading"), 15 | ("h6", "Heading"), 16 | ("p", "Text"), 17 | ("ul", "ul"), 18 | ("ol", "ol"), 19 | ("li", "li"), 20 | ("a", "Link"), 21 | ("code", "Code"), 22 | ], 23 | ) 24 | def test_get_component(tag, expected): 25 | """Test getting a component from the component map. 26 | 27 | Args: 28 | tag: The tag to get. 29 | expected: The expected component. 30 | """ 31 | md = Markdown.create("# Hello") 32 | assert tag in md.component_map # pyright: ignore [reportAttributeAccessIssue] 33 | assert md.get_component(tag).tag == expected 34 | 35 | 36 | def test_set_component_map(): 37 | """Test setting the component map.""" 38 | component_map = { 39 | "h1": lambda value: rx.box(rx.heading(value, as_="h1"), padding="1em"), 40 | "p": lambda value: rx.box(rx.text(value), padding="1em"), 41 | } 42 | md = Markdown.create("# Hello", component_map=component_map) 43 | 44 | # Check that the new tags have been added. 45 | assert md.get_component("h1").tag == "Box" 46 | assert md.get_component("p").tag == "Box" 47 | 48 | # Make sure the old tags are still there. 49 | assert md.get_component("h2").tag == "Heading" 50 | -------------------------------------------------------------------------------- /tests/units/middleware/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/middleware/__init__.py -------------------------------------------------------------------------------- /tests/units/middleware/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from reflex.event import Event 4 | from reflex.state import State 5 | 6 | 7 | def create_event(name): 8 | return Event( 9 | token="", 10 | name=name, 11 | router_data={ 12 | "pathname": "/", 13 | "query": {}, 14 | "token": "", 15 | "sid": "", 16 | "headers": {}, 17 | "ip": "127.0.0.1", 18 | }, 19 | payload={}, 20 | ) 21 | 22 | 23 | @pytest.fixture 24 | def event1(): 25 | return create_event(f"{State.get_name()}.hydrate") 26 | -------------------------------------------------------------------------------- /tests/units/middleware/test_hydrate_middleware.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | from pytest_mock import MockerFixture 5 | 6 | from reflex.app import App 7 | from reflex.middleware.hydrate_middleware import HydrateMiddleware 8 | from reflex.state import State, StateUpdate 9 | 10 | 11 | class TestState(State): 12 | """A test state with no return in handler.""" 13 | 14 | __test__ = False 15 | 16 | num: int = 0 17 | 18 | def test_handler(self): 19 | """Test handler.""" 20 | self.num += 1 21 | 22 | 23 | @pytest.fixture 24 | def hydrate_middleware() -> HydrateMiddleware: 25 | """Fixture creates an instance of HydrateMiddleware per test case. 26 | 27 | Returns: 28 | instance of HydrateMiddleware 29 | """ 30 | return HydrateMiddleware() 31 | 32 | 33 | @pytest.mark.asyncio 34 | async def test_preprocess_no_events(hydrate_middleware, event1, mocker: MockerFixture): 35 | """Test that app without on_load is processed correctly. 36 | 37 | Args: 38 | hydrate_middleware: Instance of HydrateMiddleware 39 | event1: An Event. 40 | mocker: pytest mock object. 41 | """ 42 | mocker.patch("reflex.state.State.class_subclasses", {TestState}) 43 | state = State() 44 | update = await hydrate_middleware.preprocess( 45 | app=App(_state=State), 46 | event=event1, 47 | state=state, 48 | ) 49 | assert isinstance(update, StateUpdate) 50 | assert update.delta == state.dict() 51 | assert not update.events 52 | -------------------------------------------------------------------------------- /tests/units/states/__init__.py: -------------------------------------------------------------------------------- 1 | """Common rx.BaseState subclasses for use in tests.""" 2 | 3 | import reflex as rx 4 | from reflex.state import BaseState 5 | 6 | from .mutation import DictMutationTestState, ListMutationTestState, MutableTestState 7 | from .upload import ( 8 | ChildFileUploadState, 9 | FileStateBase1, 10 | FileStateBase2, 11 | FileUploadState, 12 | GrandChildFileUploadState, 13 | SubUploadState, 14 | UploadState, 15 | ) 16 | 17 | 18 | class GenState(BaseState): 19 | """A state with event handlers that generate multiple updates.""" 20 | 21 | value: int 22 | 23 | def go(self, c: int): 24 | """Increment the value c times and update each time. 25 | 26 | Args: 27 | c: The number of times to increment. 28 | 29 | Yields: 30 | After each increment. 31 | """ 32 | for _ in range(c): 33 | self.value += 1 34 | yield 35 | -------------------------------------------------------------------------------- /tests/units/test_telemetry.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from packaging.version import parse as parse_python_version 3 | from pytest_mock import MockerFixture 4 | 5 | from reflex.utils import telemetry 6 | 7 | 8 | def test_telemetry(): 9 | """Test that telemetry is sent correctly.""" 10 | # Check that the user OS is one of the supported operating systems. 11 | user_os = telemetry.get_os() 12 | assert user_os is not None 13 | assert user_os in ["Linux", "Darwin", "Java", "Windows"] 14 | 15 | # Check that the CPU count and memory are greater than 0. 16 | assert telemetry.get_cpu_count() > 0 17 | 18 | # Check that the available memory is greater than 0 19 | assert telemetry.get_memory() > 0 20 | 21 | # Check that the Reflex version is not None. 22 | assert telemetry.get_reflex_version() is not None 23 | 24 | # Check that the Python version is greater than 3.7. 25 | python_version = telemetry.get_python_version() 26 | assert python_version is not None 27 | assert parse_python_version(python_version) >= parse_python_version("3.7") 28 | 29 | 30 | def test_disable(): 31 | """Test that disabling telemetry works.""" 32 | assert not telemetry._send("test", telemetry_enabled=False) 33 | 34 | 35 | @pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"]) 36 | def test_send(mocker: MockerFixture, event): 37 | httpx_post_mock = mocker.patch("httpx.post") 38 | 39 | # Mock the read_text method of Path 40 | mocker.patch( 41 | "pathlib.Path.read_text", 42 | return_value='{"project_hash": "78285505863498957834586115958872998605"}', 43 | ) 44 | 45 | mocker.patch("platform.platform", return_value="Mocked Platform") 46 | 47 | telemetry._send(event, telemetry_enabled=True) 48 | httpx_post_mock.assert_called_once() 49 | -------------------------------------------------------------------------------- /tests/units/test_testing.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the included testing tools.""" 2 | 3 | import pytest 4 | 5 | from reflex.constants import IS_WINDOWS 6 | from reflex.testing import AppHarness 7 | 8 | 9 | @pytest.mark.skip("Slow test that makes network requests.") 10 | def test_app_harness(tmp_path): 11 | """Ensure that AppHarness can compile and start an app. 12 | 13 | Args: 14 | tmp_path: pytest tmp_path fixture 15 | """ 16 | # Skip in Windows CI. 17 | if IS_WINDOWS: 18 | return 19 | 20 | def BasicApp(): 21 | import reflex as rx 22 | 23 | class State(rx.State): 24 | pass 25 | 26 | app = rx.App(_state=State) 27 | app.add_page(lambda: rx.text("Basic App"), route="/", title="index") 28 | app._compile() 29 | 30 | with AppHarness.create( 31 | root=tmp_path, 32 | app_source=BasicApp, 33 | ) as harness: 34 | assert harness.app_instance is not None 35 | assert harness.backend is not None 36 | assert harness.frontend_url is not None 37 | assert harness.frontend_process is not None 38 | assert harness.frontend_process.poll() is None 39 | 40 | assert harness.frontend_process.poll() is not None 41 | -------------------------------------------------------------------------------- /tests/units/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reflex-dev/reflex/13bedad332b46a250a77e048427245c3075c4c3b/tests/units/utils/__init__.py -------------------------------------------------------------------------------- /tests/units/vars/test_base.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping, Sequence 2 | 3 | import pytest 4 | 5 | from reflex.state import State 6 | from reflex.vars.base import computed_var, figure_out_type 7 | 8 | 9 | class CustomDict(dict[str, str]): 10 | """A custom dict with generic arguments.""" 11 | 12 | 13 | class ChildCustomDict(CustomDict): 14 | """A child of CustomDict.""" 15 | 16 | 17 | class GenericDict(dict): 18 | """A generic dict with no generic arguments.""" 19 | 20 | 21 | class ChildGenericDict(GenericDict): 22 | """A child of GenericDict.""" 23 | 24 | 25 | @pytest.mark.parametrize( 26 | ("value", "expected"), 27 | [ 28 | (1, int), 29 | (1.0, float), 30 | ("a", str), 31 | ([1, 2, 3], Sequence[int]), 32 | ([1, 2.0, "a"], Sequence[int | float | str]), 33 | ({"a": 1, "b": 2}, Mapping[str, int]), 34 | ({"a": 1, 2: "b"}, Mapping[int | str, str | int]), 35 | (CustomDict(), CustomDict), 36 | (ChildCustomDict(), ChildCustomDict), 37 | (GenericDict({1: 1}), Mapping[int, int]), 38 | (ChildGenericDict({1: 1}), Mapping[int, int]), 39 | ], 40 | ) 41 | def test_figure_out_type(value, expected): 42 | assert figure_out_type(value) == expected 43 | 44 | 45 | def test_computed_var_replace() -> None: 46 | class StateTest(State): 47 | @computed_var(cache=True) 48 | def cv(self) -> int: 49 | return 1 50 | 51 | cv = StateTest.cv 52 | assert cv._var_type is int 53 | 54 | replaced = cv._replace(_var_type=float) 55 | assert replaced._var_type is float 56 | --------------------------------------------------------------------------------