diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca57661d30..ed68eaf4419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## v0.9.5 (2026-06-10) + +### Features + +- `rx.form` `on_submit` handlers can now annotate their form-data parameter with a `TypedDict` (including `typing_extensions.NotRequired` fields). The submitted mapping is accepted by the event-argument type checker, and at component build time the form statically validates that its controls supply every required `TypedDict` field, raising `EventHandlerValueError` — with the missing and present field names — when a required field has no control with a matching static `name`/`id`. Validation is skipped when the form sets an `id` (controls may be associated externally via the HTML `form` attribute) or when any control identifier is a dynamic `Var`. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) +- Event handlers attached to JSX literals built outside a component's render scope — such as an `ErrorBoundary`'s `onError` — can now dispatch events. `addEvents` is reached through a module-level import that `EventLoopProvider` populates on each render, so dispatch no longer depends on a `useContext` hook being hoisted into the calling scope. The state and event-loop providers, previously hard-coded in the layout template, are now injected around the app root by the compiler from the `app_wraps` declared on the `Var`s that use them. ([#6447](https://github.com/reflex-dev/reflex/issues/6447)) +- Added `App.hydrate_fallback`, a component rendered during the page's hydration window (React Router's `HydrateFallback`) instead of a blank white page. It can also be configured without code through the `hydrate_fallback` config — a dotted import path to a no-arg callable returning a component, settable via the `REFLEX_HYDRATE_FALLBACK` environment variable — with the `App` argument taking precedence. Note that the fallback only covers the hydration window after the JS bundle has loaded, not the initial bundle download. ([#6630](https://github.com/reflex-dev/reflex/issues/6630)) +- Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode — taking precedence over the config-derived defaults as well as `REFLEX_HOT_RELOAD_INCLUDE_PATHS` and `REFLEX_HOT_RELOAD_EXCLUDE_PATHS`. ([#6639](https://github.com/reflex-dev/reflex/issues/6639)) + +### Bug Fixes + +- Anonymous telemetry now reports the installation and project identifiers as UUID strings rather than 128-bit integers. PostHog coerced the large integers to floats, discarding all but ~16 significant digits and risking distinct installs or apps being correlated as one. Each identifier is re-encoded to the same value (a UUID carries the same 128 bits), and a one-time PostHog `$create_alias` links an installation's pre-existing history to its new identifier so continuity is preserved. ([#6611](https://github.com/reflex-dev/reflex/issues/6611)) +- `scripts/make_pyi.py` is now a proper CLI for maintaining `pyi_hashes.json`: `--force` regenerates every default target (ignoring the incremental markers), explicit targets are merged into the registry instead of pruning it, and an unreachable last-run commit (after a branch switch or rebase) triggers a full regeneration. A new `--check` mode, wired into the pre-commit CI job, fails when a `pyi_hashes.json` entry no longer has a matching `.py` source. ([#6614](https://github.com/reflex-dev/reflex/issues/6614)) +- `State.get_var_value()` no longer silently returns a wrong value when passed a Var operation — an arithmetic/concatenation expression such as `State.a + State.b`, or an indexed/item access such as `State.items[0]`. Previously it resolved the state and field of the operation's *first* operand and returned that field's value instead of the operation's result. It now raises `UnretrievableVarValueError`, consistent with how it already handled vars not associated with any state. Plain field and computed-var references continue to resolve as before. ([#6633](https://github.com/reflex-dev/reflex/issues/6633)) + +### Performance + +- Speed up reading mutable state vars (lists, dicts, dataclasses) through `MutableProxy`. The per-element check that detects `dataclasses.asdict`/`astuple` recursion now reads `frame.f_code.co_filename` directly instead of calling `inspect.getfile()`, cutting proxy read overhead by roughly 3-4x on large containers without changing behavior. ([#6600](https://github.com/reflex-dev/reflex/issues/6600)) + +### Miscellaneous + +- Report the versions of the first-party Reflex subpackages shipped with Reflex (`reflex-base`, the `reflex-components-*` family and `reflex-hosting-cli`) in anonymous telemetry via a new `reflex_package_version` field. The set is derived from Reflex's own declared dependencies, so unrelated third-party `reflex-*` packages are never reported. Now that Reflex is split across many independently-versioned packages, the single `reflex_version` field no longer reflects the full install. ([#6610](https://github.com/reflex-dev/reflex/issues/6610)) + + ## v0.9.4 (2026-06-03) ### Deprecations diff --git a/news/6301.feature.md b/news/6301.feature.md deleted file mode 100644 index 4ef22edcf29..00000000000 --- a/news/6301.feature.md +++ /dev/null @@ -1 +0,0 @@ -`rx.form` `on_submit` handlers can now annotate their form-data parameter with a `TypedDict` (including `typing_extensions.NotRequired` fields). The submitted mapping is accepted by the event-argument type checker, and at component build time the form statically validates that its controls supply every required `TypedDict` field, raising `EventHandlerValueError` — with the missing and present field names — when a required field has no control with a matching static `name`/`id`. Validation is skipped when the form sets an `id` (controls may be associated externally via the HTML `form` attribute) or when any control identifier is a dynamic `Var`. diff --git a/news/6447.feature.md b/news/6447.feature.md deleted file mode 100644 index e3c8b383477..00000000000 --- a/news/6447.feature.md +++ /dev/null @@ -1 +0,0 @@ -Event handlers attached to JSX literals built outside a component's render scope — such as an `ErrorBoundary`'s `onError` — can now dispatch events. `addEvents` is reached through a module-level import that `EventLoopProvider` populates on each render, so dispatch no longer depends on a `useContext` hook being hoisted into the calling scope. The state and event-loop providers, previously hard-coded in the layout template, are now injected around the app root by the compiler from the `app_wraps` declared on the `Var`s that use them. diff --git a/news/6600.performance.md b/news/6600.performance.md deleted file mode 100644 index 0a60d36edf0..00000000000 --- a/news/6600.performance.md +++ /dev/null @@ -1 +0,0 @@ -Speed up reading mutable state vars (lists, dicts, dataclasses) through `MutableProxy`. The per-element check that detects `dataclasses.asdict`/`astuple` recursion now reads `frame.f_code.co_filename` directly instead of calling `inspect.getfile()`, cutting proxy read overhead by roughly 3-4x on large containers without changing behavior. diff --git a/news/6610.misc.md b/news/6610.misc.md deleted file mode 100644 index 44a0f750a0c..00000000000 --- a/news/6610.misc.md +++ /dev/null @@ -1 +0,0 @@ -Report the versions of the first-party Reflex subpackages shipped with Reflex (`reflex-base`, the `reflex-components-*` family and `reflex-hosting-cli`) in anonymous telemetry via a new `reflex_package_version` field. The set is derived from Reflex's own declared dependencies, so unrelated third-party `reflex-*` packages are never reported. Now that Reflex is split across many independently-versioned packages, the single `reflex_version` field no longer reflects the full install. diff --git a/news/6611.bugfix.md b/news/6611.bugfix.md deleted file mode 100644 index b74d18cff6d..00000000000 --- a/news/6611.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Anonymous telemetry now reports the installation and project identifiers as UUID strings rather than 128-bit integers. PostHog coerced the large integers to floats, discarding all but ~16 significant digits and risking distinct installs or apps being correlated as one. Each identifier is re-encoded to the same value (a UUID carries the same 128 bits), and a one-time PostHog `$create_alias` links an installation's pre-existing history to its new identifier so continuity is preserved. diff --git a/news/6614.bugfix.md b/news/6614.bugfix.md deleted file mode 100644 index e25d0c004ed..00000000000 --- a/news/6614.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -`scripts/make_pyi.py` is now a proper CLI for maintaining `pyi_hashes.json`: `--force` regenerates every default target (ignoring the incremental markers), explicit targets are merged into the registry instead of pruning it, and an unreachable last-run commit (after a branch switch or rebase) triggers a full regeneration. A new `--check` mode, wired into the pre-commit CI job, fails when a `pyi_hashes.json` entry no longer has a matching `.py` source. diff --git a/news/6630.feature.md b/news/6630.feature.md deleted file mode 100644 index dd43dc15e10..00000000000 --- a/news/6630.feature.md +++ /dev/null @@ -1 +0,0 @@ -Added `App.hydrate_fallback`, a component rendered during the page's hydration window (React Router's `HydrateFallback`) instead of a blank white page. It can also be configured without code through the `hydrate_fallback` config — a dotted import path to a no-arg callable returning a component, settable via the `REFLEX_HYDRATE_FALLBACK` environment variable — with the `App` argument taking precedence. Note that the fallback only covers the hydration window after the JS bundle has loaded, not the initial bundle download. diff --git a/news/6633.bugfix.md b/news/6633.bugfix.md deleted file mode 100644 index f29a7e3d0b6..00000000000 --- a/news/6633.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -`State.get_var_value()` no longer silently returns a wrong value when passed a Var operation — an arithmetic/concatenation expression such as `State.a + State.b`, or an indexed/item access such as `State.items[0]`. Previously it resolved the state and field of the operation's *first* operand and returned that field's value instead of the operation's result. It now raises `UnretrievableVarValueError`, consistent with how it already handled vars not associated with any state. Plain field and computed-var references continue to resolve as before. diff --git a/news/6639.feature.md b/news/6639.feature.md deleted file mode 100644 index b90767865f4..00000000000 --- a/news/6639.feature.md +++ /dev/null @@ -1 +0,0 @@ -Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode — taking precedence over the config-derived defaults as well as `REFLEX_HOT_RELOAD_INCLUDE_PATHS` and `REFLEX_HOT_RELOAD_EXCLUDE_PATHS`. diff --git a/packages/reflex-base/CHANGELOG.md b/packages/reflex-base/CHANGELOG.md index ed87900f922..da9f474c718 100644 --- a/packages/reflex-base/CHANGELOG.md +++ b/packages/reflex-base/CHANGELOG.md @@ -1,3 +1,24 @@ +## v0.9.5 (2026-06-10) + +### Features + +- Event-argument type checking now treats a mapping-style payload as compatible with a `TypedDict`-annotated callback parameter, scoped narrowly to `on_submit` triggers whose payload is a `Mapping[str, ...]` so unrelated mapping events are unaffected. Adds the `FORM_SUBMIT_MAPPING` type var (exposed on the event namespace and `pyi_generator`'s default imports) and a `Component._is_form_control` class marker that a component sets to declare it contributes a named field to form submission data. ([#6301](https://github.com/reflex-dev/reflex/issues/6301)) +- `VarData` gained an `app_wraps` field so a `Var` can declare the app-level wrapper components it requires; the compiler injects them around the app root, deduped by `(priority, tag)`. This is how the state and event-loop providers now reach the React tree, since event dispatch reaches `addEvents` via a module-level import (`Imports.EVENTS`) rather than a hoisted hook. The still-reactive `connectErrors` value moves to its own `CONNECT_ERRORS` import/hook, and `Component` deep copies now drop the render cache so compile-time clones (e.g. the app-root wrapper chain) render their mutated children. ([#6447](https://github.com/reflex-dev/reflex/issues/6447)) +- Added a `hydrate_fallback` config option (settable via the `REFLEX_HYDRATE_FALLBACK` environment variable), a dotted import path to a callable returning the component shown while the page is hydrating. The app root template now emits a React Router `HydrateFallback` export when a fallback is provided, and the import-path resolution shared with `extra_overlay_function` resolves nested module paths correctly. ([#6630](https://github.com/reflex-dev/reflex/issues/6630)) +- Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode. ([#6639](https://github.com/reflex-dev/reflex/issues/6639)) + +### Bug Fixes + +- The `reflex_base.utils.pyi_generator` build-hook entrypoint no longer rewrites `pyi_hashes.json`: it only emits the `.pyi` stubs bundled in the wheel, so building a component package (or the build triggered by `uv sync`) no longer wipes the hash registry down to a single package's entries. The scanner also tolerates source-less modules (e.g. an empty `__init__.py`) instead of raising `OSError` on Python < 3.13. ([#6614](https://github.com/reflex-dev/reflex/issues/6614)) +- Fixed `State.router.url` reflecting a stale query string after the URL was changed with `window.history.replaceState`/`pushState` (e.g. from `rx.call_script`). React Router's location does not observe direct history manipulation, so the query and hash are now read from the live `window.location` when building `router_data`, and the next event sent to the backend reports the correct URL (the path stays basename-relative so `frontend_path` is not applied twice; embedded apps keep using the in-widget memory router). A direct history mutation is intentionally not a navigation and does not itself emit an event — use `rx.redirect(..., replace=True)` when you need the URL change to update the router reactively and trigger `on_load`. ([#6625](https://github.com/reflex-dev/reflex/issues/6625)) +- pyi_generator no longer includes underscore-prefixed props in generated .pyi files. ([#6628](https://github.com/reflex-dev/reflex/issues/6628)) +- Frontend-only events (e.g. `rx.toast`, `rx.redirect`) returned from a middleware's `preprocess` are now emitted to the client instead of being enqueued on the backend event queue, where they had no registered handler and raised `KeyError`. The frontend/backend split that already applied to handler-yielded events is now shared via a `_route_events` helper and applied to middleware-preprocess updates too. ([#6644](https://github.com/reflex-dev/reflex/issues/6644)) + +### Performance + +- Speed up component creation by resolving field defaults lazily (via class-level descriptors) instead of eagerly on every instance, caching each component class's event triggers, and memoizing `to_camel_case`. ([#6576](https://github.com/reflex-dev/reflex/issues/6576)) + + ## v0.9.4 (2026-06-03) ### Deprecations diff --git a/packages/reflex-base/news/6301.feature.md b/packages/reflex-base/news/6301.feature.md deleted file mode 100644 index 4d026f3cba6..00000000000 --- a/packages/reflex-base/news/6301.feature.md +++ /dev/null @@ -1 +0,0 @@ -Event-argument type checking now treats a mapping-style payload as compatible with a `TypedDict`-annotated callback parameter, scoped narrowly to `on_submit` triggers whose payload is a `Mapping[str, ...]` so unrelated mapping events are unaffected. Adds the `FORM_SUBMIT_MAPPING` type var (exposed on the event namespace and `pyi_generator`'s default imports) and a `Component._is_form_control` class marker that a component sets to declare it contributes a named field to form submission data. diff --git a/packages/reflex-base/news/6447.feature.md b/packages/reflex-base/news/6447.feature.md deleted file mode 100644 index be617a8a9a0..00000000000 --- a/packages/reflex-base/news/6447.feature.md +++ /dev/null @@ -1 +0,0 @@ -`VarData` gained an `app_wraps` field so a `Var` can declare the app-level wrapper components it requires; the compiler injects them around the app root, deduped by `(priority, tag)`. This is how the state and event-loop providers now reach the React tree, since event dispatch reaches `addEvents` via a module-level import (`Imports.EVENTS`) rather than a hoisted hook. The still-reactive `connectErrors` value moves to its own `CONNECT_ERRORS` import/hook, and `Component` deep copies now drop the render cache so compile-time clones (e.g. the app-root wrapper chain) render their mutated children. diff --git a/packages/reflex-base/news/6576.performance.md b/packages/reflex-base/news/6576.performance.md deleted file mode 100644 index e55e226cbc0..00000000000 --- a/packages/reflex-base/news/6576.performance.md +++ /dev/null @@ -1 +0,0 @@ -Speed up component creation by resolving field defaults lazily (via class-level descriptors) instead of eagerly on every instance, caching each component class's event triggers, and memoizing `to_camel_case`. diff --git a/packages/reflex-base/news/6614.bugfix.md b/packages/reflex-base/news/6614.bugfix.md deleted file mode 100644 index 5477e0f618d..00000000000 --- a/packages/reflex-base/news/6614.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -The `reflex_base.utils.pyi_generator` build-hook entrypoint no longer rewrites `pyi_hashes.json`: it only emits the `.pyi` stubs bundled in the wheel, so building a component package (or the build triggered by `uv sync`) no longer wipes the hash registry down to a single package's entries. The scanner also tolerates source-less modules (e.g. an empty `__init__.py`) instead of raising `OSError` on Python < 3.13. diff --git a/packages/reflex-base/news/6625.bugfix.md b/packages/reflex-base/news/6625.bugfix.md deleted file mode 100644 index b45d2e29228..00000000000 --- a/packages/reflex-base/news/6625.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Fixed `State.router.url` reflecting a stale query string after the URL was changed with `window.history.replaceState`/`pushState` (e.g. from `rx.call_script`). React Router's location does not observe direct history manipulation, so the query and hash are now read from the live `window.location` when building `router_data`, and the next event sent to the backend reports the correct URL (the path stays basename-relative so `frontend_path` is not applied twice; embedded apps keep using the in-widget memory router). A direct history mutation is intentionally not a navigation and does not itself emit an event — use `rx.redirect(..., replace=True)` when you need the URL change to update the router reactively and trigger `on_load`. diff --git a/packages/reflex-base/news/6628.bugfix.md b/packages/reflex-base/news/6628.bugfix.md deleted file mode 100644 index d3e112f68c8..00000000000 --- a/packages/reflex-base/news/6628.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -pyi_generator no longer includes underscore-prefixed props in generated .pyi files. \ No newline at end of file diff --git a/packages/reflex-base/news/6630.feature.md b/packages/reflex-base/news/6630.feature.md deleted file mode 100644 index f15d1cdd6ef..00000000000 --- a/packages/reflex-base/news/6630.feature.md +++ /dev/null @@ -1 +0,0 @@ -Added a `hydrate_fallback` config option (settable via the `REFLEX_HYDRATE_FALLBACK` environment variable), a dotted import path to a callable returning the component shown while the page is hydrating. The app root template now emits a React Router `HydrateFallback` export when a fallback is provided, and the import-path resolution shared with `extra_overlay_function` resolves nested module paths correctly. diff --git a/packages/reflex-base/news/6639.feature.md b/packages/reflex-base/news/6639.feature.md deleted file mode 100644 index cebaafa0b79..00000000000 --- a/packages/reflex-base/news/6639.feature.md +++ /dev/null @@ -1 +0,0 @@ -Added the `REFLEX_HOT_RELOAD_OVERRIDE_PATHS` environment variable, a colon-separated list of paths that, when set, fully replaces the paths watched for hot reload in dev mode. diff --git a/packages/reflex-base/news/6644.bugfix.md b/packages/reflex-base/news/6644.bugfix.md deleted file mode 100644 index 63ba91423ba..00000000000 --- a/packages/reflex-base/news/6644.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Frontend-only events (e.g. `rx.toast`, `rx.redirect`) returned from a middleware's `preprocess` are now emitted to the client instead of being enqueued on the backend event queue, where they had no registered handler and raised `KeyError`. The frontend/backend split that already applied to handler-yielded events is now shared via a `_route_events` helper and applied to middleware-preprocess updates too. diff --git a/packages/reflex-components-lucide/CHANGELOG.md b/packages/reflex-components-lucide/CHANGELOG.md new file mode 100644 index 00000000000..ea0425e6c23 --- /dev/null +++ b/packages/reflex-components-lucide/CHANGELOG.md @@ -0,0 +1,5 @@ +## v1.0.2 (2026-06-10) + +### Bug Fixes + +- `rx.icon` with a static name now emits a deep per-icon import (`import LucideWifiOff from "lucide-react/dist/esm/icons/wifi-off.mjs"`) instead of a named import from the `lucide-react` barrel. The barrel import was harmless on its own, but once an app's module graph also contained `lucide-react/dynamic.mjs` (pulled in by dynamic `Var`-tagged icons, and by the connection overlay mounted on every page), esbuild's dev dep-optimizer rewrote the barrel to statically import every icon chunk — making each page load fetch the entire ~1700-icon library. Deep imports load only the icons actually used. The dynamic `Var` path still resolves through `lucide-react/dynamic.mjs` and is unchanged. ([#6628](https://github.com/reflex-dev/reflex/issues/6628)) diff --git a/packages/reflex-components-lucide/news/6628.bugfix.md b/packages/reflex-components-lucide/news/6628.bugfix.md deleted file mode 100644 index aa933aa078e..00000000000 --- a/packages/reflex-components-lucide/news/6628.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -`rx.icon` with a static name now emits a deep per-icon import (`import LucideWifiOff from "lucide-react/dist/esm/icons/wifi-off.mjs"`) instead of a named import from the `lucide-react` barrel. The barrel import was harmless on its own, but once an app's module graph also contained `lucide-react/dynamic.mjs` (pulled in by dynamic `Var`-tagged icons, and by the connection overlay mounted on every page), esbuild's dev dep-optimizer rewrote the barrel to statically import every icon chunk — making each page load fetch the entire ~1700-icon library. Deep imports load only the icons actually used. The dynamic `Var` path still resolves through `lucide-react/dynamic.mjs` and is unchanged.