Breaking Changes
router.session.client_ipnow ignores untrustedX-Forwarded-Forheaders by default. Configuretrusted_proxy_hopsto use forwarded client addresses from a trusted proxy chain. (#7614)
Features
-
Add an experimental, default-off
react_compilerconfiguration flag to enable React Compiler memoization for generated components in development and production. (#7234) -
Form
on_submithandlers can annotate their form data asrx.form.FormDatato receive every value of fields that share a name, in submission order; adictannotation still keeps each name's last value. In aTypedDictannotation, alist[str]field collects every value of its name and aboolfield isFalsewhen nothing was submitted, such as for an unchecked checkbox, or when the value is false-like, such as"false","off"or"0"; forlist[str] | Noneandbool | Nonefields an unsubmitted value isNone, and an unsubmittedNotRequiredfield is left out. In aTypedDict, a list fieldrangealso collects therange[]values a two-thumbrx.slider(name="range")submits.@rx.event def handle_submit(self, form_data: rx.form.FormData[str, str]): self.toppings = form_data.getlist("topping") ``` ([#7386](https://github.com/reflex-dev/reflex/issues/7386))
-
A page's
on_loadaccepts any sequence of events, e.g. a tuple, like a list of them. (#7402) -
Detect a frontend built for another version of the app when it connects to the backend. The compiled frontend now carries a hash of the state tree (state names, frontend vars and event handler names) and sends it on every socket connect. On a mismatch, a production page reloads once (sharing the
REFLEX_AUTO_RELOAD_COOLDOWNwith the reload on frontend errors) and otherwise shows a "new version available" banner with a reload button; in dev mode it only logs a console warning. Load balancers serving several backend versions still need sticky sessions. (#7553) -
Expose
rx.moment_providerfor sharing locale, timezone, formatting, and timer defaults across Moment components. (#7591)
Bug Fixes
- Use atomic writes and OS locks to protect shared JSON metadata from concurrent Reflex processes. When locks are unavailable (for example, with a read-only
REFLEX_DIR), writes remain atomic but concurrent updates may overwrite each other. (#7052) - Load backend apps by their configured module name so state updates work even when the app package has no
__init__.py. (#7258) - A
stateful_pages.jsonmarker that is not valid UTF-8 is now treated as corrupt and rebuilt, instead of raisingUnicodeDecodeErroron every backend start. (#7404) - Type checkers see the database helpers of
reflex.model(rx.session,rx.asession,rx.Model,ModelRegistry, ...) as defined, rather than as unions with the stand-ins used when the db extra is missing. (#7406) - Fix a global
rx._x.client_statereader that mounts after the value was set, such as one behind anrx.cond, keeping the stale value when the value is later set back to its default. (#7407) - Fix
TypeError: refs._client_state_set... is not a functionwhen the backend pushes a globalrx._x.client_statevalue before any component using it has mounted, such as one behind anrx.condor a page that waits for hydration. The value is now kept for the first component that mounts. (#7407) - Fix unawaited coroutine warnings when a state delta filter withholds an async uncached computed variable. (#7410)
- Fix installing Reflex source distributions with uv by excluding references to workspace packages that are not included in the archive. (#7411)
- Cancel unfinished page-load event chains when the client disconnects, including hydration that has not yet started. Independent background events continue running. (#7415)
- The backend log for an unknown frontend substate now says the substate was skipped instead of reporting a failed state update. The frontend exception handler docs describe how to resolve the mismatch. (#7435)
reflex.model(andrx.ModelRegistry,rx.session,rx.Model) can be imported withoutgreenlet, which SQLAlchemy 2.1 only installs with itsasyncioextra; onlyrx.asessionandget_async_enginerequire it now. (#7440)- Detect state reads from the new shared state store when checking that an app that reads state defines an
rx.Statesubclass. (#7441) - Raise an actionable error when a legacy string passed to
App.modify_state()has a missing or unknown state path. Userx.BaseStateToken(ident=client_token, cls=StateClass)to select the state explicitly. (#7443) - Production exports,
reflex run --env prod, and preview startup that share a.webdirectory now wait for one another instead of overwriting each other's compiled frontend and archives. (#7453) - Mutating a nested value reached through
dict.values()ordict.items()on a state var now marks the var dirty, so the UI and cached vars depending on it update. (#7500) reflex runno longer logs "Unexpected exit from worker-1" when the dev backend is stopped with SIGTERM or SIGINT. (#7538)- Cap the automatically selected number of Redis-backed backend workers at eight. Set
GRANIAN_WORKERSto override the default. (#7542) reflex initno longer refuses to reinitialize an existing app just because its project directory is namedreflex; the check now applies to theapp_nameset inrxconfig.py. (#7554)- Add development frontend packages with
-D, so npm saves them underdevDependenciesand source-only hot reloads no longer reinstall frontend packages. (#7566) - Cached computed vars that depend on client storage (
rx.LocalStorage,rx.SessionStorage,rx.Cookie) are now evaluated once per page load instead of twice. (#7568) - A background task that re-enters an expired session now rehydrates the loaded state tree, so the browser no longer shows stale values for those states. (#7585)
- Repeated request headers are no longer collapsed to their last value: repeated
cookiefields are joined with"; "and any other repeated field with", ", sorouter.headers.cookiekeeps every cookie. (#7588) - Websocket connections now accept request header values containing bytes that are not valid UTF-8. (#7602)
- Avoid false type errors when hydrating or assigning strings to vars annotated with
Cookie,LocalStorage, orSessionStorage. (#7603) - Clarify the warning shown when Bun is below the minimum supported version. (#7639)
Performance
- Production backend workers on Linux are now forked from a supervisor that has already imported the app, so they share its memory instead of each importing the app again, which cuts memory use and startup time. Set
REFLEX_BACKEND_START_METHOD=spawnfor apps that are not fork-safe. (#7079) - The
reflex runbackend reload worker is now forked from the supervisor on Linux instead of started through a forkserver, so framework code is shared copy-on-write and the extra forkserver processes are gone. SetREFLEX_BACKEND_START_METHOD=spawnorREFLEX_STRICT_HOT_RELOAD=1for apps that are not fork-safe. (#7113) - Speed up deployment archive creation by reducing filesystem checks for excluded files and avoiding recompression of precompressed frontend assets. (#7450)